我正在使用python 2.7
我想创建一个围绕fcntl.flock()的包装函数,它会在设置的时间间隔后超时:
wrapper_function(timeout):
我试过调用另一个线程并使用thread.join(timeout)但似乎fcntl.flock()继续阻塞:
def GetLock(self, timeout):
"""Returns true if lock is aquired, false if lock is already in use"""
self.__lock_file = open('proc_lock', 'w')
def GetLockOrTimeOut():
print 'ProcessLock: Acquiring Lock'
fcntl.flock(self.__lock_file.fileno(), fcntl.LOCK_EX)
print 'ProcessLock: Lock Acquired'
thread = threading.Thread(target=GetLockOrTimeOut)
thread.start()
thread.join(timeout)
if thread.isAlive():
print 'GetLock timed out'
return False
else:
return True
我已经研究了终止线程的解决方案,最流行的解决方案似乎是对threading.thread进行子类化并添加一个功能来引发线程中的异常。但是,我遇到一个link,说这个方法不适用于本机调用,我很确定fcntl.flock()正在调用本机函数。建议?
上下文:我使用文件锁来创建单个实例应用程序,但我不希望应用程序的第二个实例坐下来并挂起,直到第一个实例终止。
答案 0 :(得分:24)
系统调用的超时是通过信号完成的。当信号发生时,大多数阻塞系统调用都会返回EINTR,因此您可以使用alarm
来实现超时。
这是一个适用于大多数系统调用的上下文管理器,如果花费的时间过长,会导致阻塞系统调用引发IOError。
import signal, errno
from contextlib import contextmanager
import fcntl
@contextmanager
def timeout(seconds):
def timeout_handler(signum, frame):
pass
original_handler = signal.signal(signal.SIGALRM, timeout_handler)
try:
signal.alarm(seconds)
yield
finally:
signal.alarm(0)
signal.signal(signal.SIGALRM, original_handler)
with timeout(1):
f = open("test.lck", "w")
try:
fcntl.flock(f.fileno(), fcntl.LOCK_EX)
except IOError, e:
if e.errno != errno.EINTR:
raise e
print "Lock timed out"
答案 1 :(得分:7)
我确信有几种方法,但如何使用非阻塞锁?经过n次尝试后,放弃并退出?
要使用非阻塞锁定,请包含fcntl.LOCK_NB
标记,如:
fcntl.flock(self.__lock_file.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB)
答案 2 :(得分:3)
我很喜欢炮轰在这里,因为尝试使用超时阻止锁定需要更改全局状态,这使得你的程序更难以推理,特别是如果涉及线程。
您可以分叉子流程并按上述方式实施警报,或者您可以执行http://man7.org/linux/man-pages/man1/flock.1.html
import subprocess
def flock_with_timeout(fd, timeout, shared=True):
rc = subprocess.call(['flock', '--shared' if shared else '--exclusive', '--timeout', str(timeout), str(fd)])
if rc != 0:
raise Exception('Failed to take lock')
如果你有一个足够新的flock版本,你可以使用-E
为命令指定不同的退出代码,否则成功,但是在超时后无法锁定,所以你可以知道命令是否失败反而出于其他原因。
答案 3 :(得分:0)
对于Python 3.5 +,Glenn Maynard's solution由于PEP-475而不再起作用。这是修改后的版本:
import signal, errno
from contextlib import contextmanager
import fcntl
@contextmanager
def timeout(seconds):
def timeout_handler(signum, frame):
# Now that flock retries automatically when interrupted, we need
# an exception to stop it
# This exception will propagate on the main thread, make sure you're calling flock there
raise InterruptedError
original_handler = signal.signal(signal.SIGALRM, timeout_handler)
try:
signal.alarm(seconds)
yield
finally:
signal.alarm(0)
signal.signal(signal.SIGALRM, original_handler)
with timeout(1):
f = open("test.lck", "w")
try:
fcntl.flock(f.fileno(), fcntl.LOCK_EX)
except InterruptedError:
# Catch the exception raised by the handler
# If we weren't raising an exception, flock would automatically retry on signals
print("Lock timed out")