Linux下Python的系统级互斥锁
有没有简单的方法可以在Linux上为Python创建一个系统级的互斥锁?这里的“系统级”是指这个互斥锁会被一组Python 进程使用;这和传统的互斥锁不同,后者是用于同一个进程内的一组线程。
补充说明:我不太确定Python的multiprocessing
包是否适合我的需求。例如,我可以在两个不同的解释器中执行以下命令:
from multiprocessing import Lock
L = Lock()
L.acquire()
当我在两个不同的解释器中同时执行这些命令时,我希望其中一个会被挂起。但实际上,两个都没有挂起;看起来它们并没有获取到同一个互斥锁。
6 个回答
可以试试这个叫做 ilock 的库:
from ilock import ILock
with ILock('Unique lock name'):
# The code should be run as a system-wide single instance
...
我的回答和其他人的有些重叠,但我想补充一些大家可以直接复制粘贴的内容,我经常会这样做。
class Locker:
def __enter__ (self):
self.fp = open("./lockfile.lck")
fcntl.flock(self.fp.fileno(), fcntl.LOCK_EX)
def __exit__ (self, _type, value, tb):
fcntl.flock(self.fp.fileno(), fcntl.LOCK_UN)
self.fp.close()
然后可以这样使用:
print("waiting for lock")
with Locker():
print("obtained lock")
time.sleep(5.0)
为了测试,可以先运行 touch lockfile.lck
,然后在两个或更多不同的终端中(从同一个目录)运行上面的代码。
更新:smwikipedia提到我的解决方案是针对Unix系统的。最近我需要一个可以在不同系统上使用的版本,于是想出了以下这个,灵感来自一个随机的GitHub项目。我不确定seek()调用是否必要,但它们在这里是因为Windows的API会锁定文件中的特定位置。如果你只是用这个文件来锁定,可能可以去掉这些seek。
if os.name == "nt":
import msvcrt
def portable_lock(fp):
fp.seek(0)
msvcrt.locking(fp.fileno(), msvcrt.LK_LOCK, 1)
def portable_unlock(fp):
fp.seek(0)
msvcrt.locking(fp.fileno(), msvcrt.LK_UNLCK, 1)
else:
import fcntl
def portable_lock(fp):
fcntl.flock(fp.fileno(), fcntl.LOCK_EX)
def portable_unlock(fp):
fcntl.flock(fp.fileno(), fcntl.LOCK_UN)
class Locker:
def __enter__(self):
self.fp = open("./lockfile.lck")
portable_lock(self.fp)
def __exit__(self, _type, value, tb):
portable_unlock(self.fp)
self.fp.close()
在“传统”的Unix系统中,解决这个问题的方法是使用文件锁。你可以用 lockf(3)
来锁定文件的某些部分,这样其他程序就不能编辑这些部分了;很多时候,人们会把它当作进程之间的互斥锁来使用。在Python中,类似的功能可以通过 fcntl.lockf 来实现。
通常情况下,你会把锁定进程的PID(进程ID)写入锁文件,这样如果进程在持有锁的时候崩溃了,就能识别并解决死锁问题。
这样做可以满足你的需求,因为这个锁是在一个全局的命名空间中(也就是文件系统),所有进程都能访问到。这个方法的好处是,非Python的程序也可以参与到你的锁定机制中。不过,缺点是你需要有一个地方来存放这个锁文件;而且,有些文件系统并不能正确地实现锁定,所以可能会出现锁定失败的情况。总的来说,有得有失。