fcntl.flock - 如何实现超时?

J C*_*per 11 python multithreading

我正在使用python 2.7

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

wrapper_function(timeout):
Run Code Online (Sandbox Code Playgroud)

我试过调用另一个线程并使用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
Run Code Online (Sandbox Code Playgroud)

我已经研究了终止线程的解决方案,最流行的解决方案似乎是对threading.thread进行子类化并添加一个功能来引发线程中的异常.但是,我遇到一个链接,说这个方法不适用于本机调用,我很确定fcntl.flock()正在调用本机函数.建议?

上下文:我使用文件锁来创建单个实例应用程序,但我不希望应用程序的第二个实例坐下来并挂起,直到第一个实例终止.

Gle*_*ard 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"
Run Code Online (Sandbox Code Playgroud)

  • 当fcntl.flock不一定被主线程调用时,有没有办法解决这个问题? (2认同)
  • 请注意,这在 [PEP-475](https://www.python.org/dev/peps/pep-0475/) (PY3.5+) 之后不再有效:如果失败,`flock` 现在会自动重试,因为的一个信号。修复方法是从“timeout_handler”引发异常,例如“raise InterruptedException”。它将从中断的调用中传播(假设它是主线程),您可以在那里捕获它。 (2认同)

jco*_*ctx 8

我确定有几种方法,但如何使用非阻塞锁?经过n次尝试,放弃并退出?

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

fcntl.flock(self.__lock_file.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB)
Run Code Online (Sandbox Code Playgroud)

  • 那不是一个好主意.阻塞锁可以使内核变得更加智能:它可以在锁可用时立即唤醒您的进程,以避免不必要的延迟,避免网络文件系统上的网络垃圾邮件等. (2认同)

rem*_*ram 5

对于 Python 3.5+,Glenn Maynard 的解决方案由于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")
Run Code Online (Sandbox Code Playgroud)