flock-如何实现超时?

2024-05-20 11:12:20 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在使用Python2.7

我想在fcntl.flock()周围创建一个包装函数,该函数将在设置的时间间隔后超时:

wrapper_function(timeout):

我尝试调用另一个线程并使用thread.join(超时),但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()调用的是本机函数。建议?

上下文:我使用一个文件锁来创建一个单实例应用程序,但是我不希望应用程序的第二个实例在第一个实例终止之前一直挂起。


Tags: 实例函数selflockifisdeftimeout
3条回答

我肯定有几种方法,但是使用非阻塞锁怎么样?在尝试了n次之后,放弃并退出?

要使用非阻塞锁,请包含fcntl.LOCK_NB标志,如下所示:

fcntl.flock(self.__lock_file.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB)

我是一个热衷于在这里大肆攻击的人,因为试图在超时情况下执行阻塞锁需要更改全局状态,这使得对程序进行推理变得更加困难,尤其是在涉及线程的情况下。

您可以派生一个子进程并实现上述警报,也可以只执行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为命令指定不同的退出代码,否则会成功,但在超时后无法获取锁,因此您可以知道该命令是否因其他原因而失败。

系统调用的超时是通过信号完成的。当信号发生时,大多数阻塞系统调用都会返回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"

相关问题 更多 >