2015-03-02

[Python]Unixドメインソケットを使って排他制御してみた

Unixドメインソケットを使ったプロセスの同時実行制御(排他制御)について考えてみました。

以下のサイトで紹介されている 抽象名前空間 というものを利用することにしました。 UNIXドメインソケットのアドレスの種類Redis コア開発者 @pnoordhuis のツイートで Unix ドメインソケットに abstract socket address なるソケットアドレスがあることを知る。 ということで Unix ドメインソケットのソケットアドレスの種類を調べてみた。 ソケットアドレスの種類 Unix ドメインソケットでは大きく分けて次の3種類のアドレスで通信できる。 ファイルシステムパス名(path…https://siguniang.wordpress.com/2012/04/29/unix-domain-socket-address-types/

ロックファイルを使うと意図せずにロックファイルが残ったときに実行ができなくなる可能性がありますが、 この方法ならプロセスとだけ結びつく(毎回ソケットファイルを削除する必要がない)ためそういった心配がありません。

あと対象のパスの存在や書込権限を考慮しなくてよいのは大きなメリットですね。

以下は排他的に「1秒おきに実行中であることを5回表示してくれる」すてきなプログラムです。

only.pyというすてきな名前で保存します。

import socket # 先頭をNULLにすると抽象名前空間というものになるらしいです SOCKET_NAME = '\0test' def main(): s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) try: # 既にbind中だと例外が発生する s.bind(SOCKET_NAME) import time for i in range(5): print '実行中だよ(%s)\n' % i time.sleep(1) except socket.error: # 例外が発生したらほかで実行されているということ print('だめだよ!') finally: # とりあえず閉じる s.close() if __name__ == '__main__': main()

※Linuxでしか動作しないようです。

3並列で実行するとこんな感じになります。すごい見づらい

  • $ ./only.py 実行中だよ(0) 実行中だよ(1) 実行中だよ(2) 実行中だよ(3) 実行中だよ(4) $ .only.py だめだよ! $ .only.py だめだよ! $ .only.py だめだよ! $ .only.py だめだよ! $ .only.py だめだよ! $ .only.py だめだよ! $ .only.py だめだよ! $ .only.py だめだよ! $ .only.py だめだよ! $ .only.py だめだよ!
  • $ .only.py だめだよ! $ .only.py だめだよ! $ .only.py だめだよ! $ .only.py だめだよ! $ .only.py だめだよ! $ ./only.py 実行中だよ(0) 実行中だよ(1) 実行中だよ(2) 実行中だよ(3) 実行中だよ(4) $ .only.py だめだよ! $ .only.py だめだよ! $ .only.py だめだよ! $ .only.py だめだよ! $ .only.py だめだよ!
  • $ .only.py だめだよ! $ .only.py だめだよ! $ .only.py だめだよ! $ .only.py だめだよ! $ .only.py だめだよ! $ .only.py だめだよ! $ .only.py だめだよ! $ .only.py だめだよ! $ .only.py だめだよ! $ .only.py だめだよ! $ ./only.py 実行中だよ(0) 実行中だよ(1) 実行中だよ(2) 実行中だよ(3) 実行中だよ(4)

こんな感じになります。(2行で1秒のイメージ)

socketというとacceptとかconnectとか通信することを考えがちですが、こういった使い方もできるんですねー

Gistにアップしました only.pyGitHub Gist: instantly share code, notes, and snippets.https://gist.github.com/righ/f1800f4d223aed72fb56

デコレータの第1引数でソケット名、第2引数でエラー時の処理を関数で渡します。 (せっかくなのでクラスベースのデコレータで実装してみました)

例えば下記のようにすると上と同じ結果になります(たぶん

import only def damedayo(): print 'だめだよ!' @only.only('\0test', damedayo) def main(): import time for i in range(5): print '実行中だよ(%s)\n' % i time.sleep(1) if __name__ == '__main__': main()