这种文件锁定方法是否可以接受?

xni*_*ine 3 python linux networking locking

我们有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)
Run Code Online (Sandbox Code Playgroud)

在我们的测试中,这种方法有效,但我想知道我们是否会遇到一些我们没有看到的问题.我意识到灾难可能是因为两台计算机同时运行exists().两台计算机不太可能同时达到这一点,因为任务在20分钟到8小时之间.

Dav*_*d Z 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
Run Code Online (Sandbox Code Playgroud)

你拥有它的程序大部分时间都可以工作,但正如所提到的,如果另一个进程在检查它的存在和调用之间移动文件,你可能会遇到问题move.我想你可以解决这个问题,但我个人建议坚持使用经过良好测试的互斥算法.(我从Andrew Tanenbaum 翻译/移植了现代操作系统的上述代码示例,但我可能在转换中引入了错误 - 只是公平警告)

顺便说一句,openLinux上的函数的手册页提供了这种文件锁定解决方案:

使用锁文件执行原子文件锁定的解决方案是在同一文件系统上创建一个唯一文件(例如,合并主机名和pid),使用link(2)建立到lockfile的链接.如果link()返回0,则锁定成功.否则,在唯一文件上使用stat(2)来检查其链接计数是否已增加到2,在这种情况下锁定也是成功的.

要在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
Run Code Online (Sandbox Code Playgroud)