2011-03-10 95 views
11

我使用Python 2.7fcntl.flock - 如何实现超时?

我想创建一个围绕fcntl.flock)的包装功能(设定的间隔后就会超时:

wrapper_function(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()调用本地函数。建议?

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

回答

21

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

+1,这是正确的做法。这也是shell实用程序['flock(1)'](http://linux.die.net/man/1/flock)的工作原理(源代码可用[ftp://ftp.kernel.org/pub /linux/utils/util-linux-ng/](ftp://ftp.kernel.org/pub/linux/utils/util-linux-ng/)) – 2011-03-10 04:37:59

+0

同意了,这是一个更好的方法。 – 2011-03-10 05:07:10

+2

当fcntl.flock不一定被主线程调用时,有没有办法把它关闭? – UsAaR33 2012-01-26 03:34:07

7

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

要使用非阻塞锁,包括fcntl.LOCK_NB标志,如:

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

这将是完美的,但你说的这个非阻塞锁在哪里? (我对python相当陌生)请从* pydoc fcntl *详细说明 – 2011-03-10 03:54:01

+1

:当操作是LOCK_SH或LOCK_EX时,它也可以与LOCK_NB按位或运算,以避免阻塞锁定获取 – 2011-03-10 03:55:41

+0

它不是一个真正的Python事物,我一直与C一起使用多达22年。 – 2011-03-10 03:58:32

1

我炮击了在这里聚集,因为试图做一个阻挡锁有超时的粉丝需要改变全局状态,这使得它更难推理程序,特别是如果线程参与。

你可以叉掉一个子进程并实现上文报警,或者你可以只exec的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') 

如果你有一群新足够的版本,你可以使用-E指定不同的退出码该命令以其他方式成功,但超时后未能锁定,因此您可以通过其他原因知道该命令是否失败。

+0

当问题出现正确的解决方案时,为什么外包需要外部实体?从python脚本调用外部shell需要内核为fork/exec对调用额外的工作,这会给你在内存范围,CPU和I/O使用方面的性能损失。想象一下,在负载较重的系统上,每秒有十几次临界区。也许这对于OP来说不是问题,但建立一个良好的实践很好。 – ArturFH 2016-11-30 17:29:00

+0

淘汰有很多优点;它的代码少,维护和外部程序已彻底调试。大概这个外部程序会照顾所有的角落案例。不要重新发明轮子... – presto8 2017-10-20 15:20:28