Python インタープリター、および基礎となる os とファイルシステムが os.rename をアトミック操作として扱い、宛先が存在する場合にエラーになると仮定すると、次のメソッドには競合状態がありません。これをLinuxマシンの本番環境で使用しています。サードパーティのライブラリを必要とせず、OS に依存しません。余分なファイルの作成を除けば、多くのユース ケースでパフォーマンス ヒットは許容範囲内です。ここで、python の関数デコレータ パターンまたは「with_statement」コンテキスト マネージャを簡単に適用して、混乱を抽象化できます。
新しいプロセス/タスクを開始する前に、lock_filename が存在しないことを確認する必要があります。
import os,time
def get_tmp_file():
filename='tmp_%s_%s'%(os.getpid(),time.time())
open(filename).close()
return filename
def do_exclusive_work():
print 'exclusive work being done...'
num_tries=10
wait_time=10
lock_filename='filename.lock'
acquired=False
for try_num in xrange(num_tries):
tmp_filename=get_tmp_file()
if not os.path.exists(lock_filename):
try:
os.rename(tmp_filename,lock_filename)
acquired=True
except (OSError,ValueError,IOError), e:
pass
if acquired:
try:
do_exclusive_work()
finally:
os.remove(lock_filename)
break
os.remove(tmp_filename)
time.sleep(wait_time)
assert acquired, 'maximum tries reached, failed to acquire lock file'
編集
os.rename が Windows 以外の OS で宛先をサイレントに上書きすることが明らかになりました。これを指摘してくれてありがとう @ akrueger!
ここから収集された回避策は次のとおりです。
os.rename を使用する代わりに、次を使用できます。
try:
if os.name != 'nt': # non-windows needs a create-exclusive operation
fd = os.open(lock_filename, os.O_WRONLY | os.O_CREAT | os.O_EXCL)
os.close(fd)
# non-windows os.rename will overwrite lock_filename silently.
# We leave this call in here just so the tmp file is deleted but it could be refactored so the tmp file is never even generated for a non-windows OS
os.rename(tmp_filename,lock_filename)
acquired=True
except (OSError,ValueError,IOError), e:
if os.name != 'nt' and not 'File exists' in str(e): raise
@ akrueger おそらく、ディレクトリベースのソリューションで十分であり、代替方法を提供するだけです。