假设您的 Python 解释器,以及底层操作系统和文件系统将 os.rename 视为原子操作,并且当目标存在时会出错,则以下方法没有竞争条件。我在 linux 机器上的生产中使用它。不需要第三方库并且不依赖于操作系统,并且除了创建额外的文件之外,对于许多用例来说,性能损失是可以接受的。您可以在这里轻松应用 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 操作系统上的目的地。感谢您指出这一点@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 您可能对基于目录的解决方案很好,只是为您提供了另一种方法。