2011-03-10 94 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