我们有10个Linux盒子,每周必须运行100个不同的任务.当我们在家时,这些计算机主要在晚上工作.我的一位同事正在开发一个项目,通过使用Python自动启动任务来优化运行时间.他的程序将读取任务列表,获取打开的任务,在文件中将该任务标记为正在进行,然后在任务完成后将任务标记为在文件中完成.任务文件将在我们的网络安装上.
我们意识到不建议有多个程序实例访问同一个文件,但我们实际上没有看到任何其他选项.虽然他正在寻找一种方法来防止两台计算机同时写入文件,但我想出了一种我自己的方法,它比我们在网上找到的方法更容易实现.我的方法是检查文件是否存在,如果不存在则等待几秒钟,然后暂时移动文件.我写了一个脚本来测试这个方法:
#!/usr/bin/env python
import time, os, shutil
from shutil import move
from os import path
fh = "testfile"
fhtemp = "testfiletemp"
while os.path.exists(fh) == False:
time.sleep(3)
move(fh, fhtemp)
f = open(fhtemp, 'w')
line = raw_input("type something: ")
print "writing to file"
f.write(line)
raw_input("hit enter to close file.")
f.close()
move(fhtemp, fh)
在我们的测试中,这种方法有效,但我想知道我们是否会遇到一些我们没有看到的问题.我意识到灾难可能是因为两台计算机同时运行exists().两台计算机不太可能同时达到这一点,因为任务在20分钟到8小时之间.
解决方法:
您基本上已经开发了二进制信号量(或互斥量)的文件系统版本.这是一个用于锁定的经过充分研究的结构,因此只要您获得正确的实现细节,它就应该有效.诀窍是获得“测试和设置”操作,或者在你的情况下“检查存在和移动”,以真正原子化.为此,我会使用这样的东西:
lock_acquired = False
while not lock_acquired:
try:
move(fh, fhtemp)
except:
sleep(3)
else:
lock_acquired = True
# do your writing
move(fhtemp, fh)
lock_acquired = False
您拥有它的程序大部分时间都可以工作,但如上所述,如果另一个进程在检查存在和移动调用之间移动文件,则可能会出现问题.我想你可以解决这个问题,但我个人建议坚持使用经过良好测试的互斥算法. (我从Andrew Tanenbaum翻译/移植了现代操作系统的上述代码示例,但我可能在转换中引入了错误 – 只是公平警告)
顺便说一句,Linux上的open函数的手册页提供了这种文件锁定解决方案:
The solution for performing atomic file locking using a lockfile is to create a unique file on the same file system (e.g., incorporating hostname and pid), use link(2) to make a link to the lockfile. If link() returns 0, the lock is successful. Otherwise, use stat(2) on the unique file to check if its link count has increased to 2, in which case the lock is also successful.
要在Python中实现它,你可以这样做:
# each instance of the process should have a different filename here
process_lockfile = '/path/to/hostname.pid.lock'
# all processes should have the same filename here
global_lockfile = '/path/to/lockfile'
# create the file if necessary (only once, at the beginning of each process)
with open(process_lockfile, 'w') as f:
f.write('\n') # or maybe write the hostname and pid
# now, each time you have to lock the file:
lock_acquired = False
while not lock_acquired:
try:
link(process_lockfile, global_lockfile)
except:
lock_acquired = (stat(process_lockfile).st_nlinks == 2)
else:
lock_acquired = True
# do your writing
unlink(global_lockfile)
lock_acquired = False