2016-04-25 26 views
0

[我有一個非常困難的時間來實現線程/進程安全的解決方案來獲取文件鎖使用Linux上的Python 3(我不在乎關於便攜式解決方案,因爲我工作的程序使用了大量的Linux內核獨佔集裝箱技術)。]尋找可靠的python進程同步技術(Linux不可移植)

閱讀http://apenwarr.ca/log/?m=201012#13後,我決定用fcntl.lockf()鎖定過程獨佔訪問的文件和寫的以下功能:

import contextlib as Contextlib 
import errno as Errno 
import fcntl as Fcntl 
import os as Os 


@Contextlib.contextmanager 
def exclusiveOpen(filename, 
        mode): 
    try: 
    fileDescriptor = Os.open(filename, 
          Os.O_WRONLY | Os.O_CREAT) 
    except OSError as e: 
    if not e.errno == Errno.EEXIST: 
     raise 

    try: 
    Fcntl.lockf(fileDescriptor, 
       Fcntl.LOCK_EX) 
    fileObject = Os.fdopen(fileDescriptor, 
          mode) 

    try: 
     yield fileObject 
    finally: 
     fileObject.flush() 
     Os.fdatasync(fileDescriptor) 
    finally: 
    Os.close(fileDescriptor) 

除此之外,我確定,它是在正確的(爲什麼不阻止在Fcntl.lockf(fileDescriptor, Fcntl.LOCK_EX)?),讓我感到不安的部分最多,是獲取fileDescriptor的地方 - 如果文件不存在,它會被創建......但是發生了什麼,如果兩個進程同時執行這個部分?兩個線程都嘗試創建文件時,沒有競爭條件的機會嗎?如果是這樣,一個人怎麼可能阻止 - 當然不是與另一個鎖文件(?),因爲它必須以相同的方式創建(?!?!)我迷路了。任何幫助是極大的讚賞。

更新:發表了另一種解決根本問題的方法。我在這種方法中看到的問題是,過程名稱不能等於現有UNIX域套接字(可能由另一個程序創建)的名稱 - 我正確嗎?

回答

1

AFAIK,創建文件時不存在競爭條件。如果兩個(或多個)進程或線程嘗試同時創建openO_CREAT標誌,而不是O_EXCL,則該文件將被創建一次,所有調用者都將在同一文件上獲得一個打開的文件描述符 - 完全你需要什麼。我假設您在Linux上使用C-Python,並且您的Python代碼將使用基礎C開放函數結束。

所以,當你的代碼執行lockf功能,您必須對現有的文件打開的文件描述符,所以只有一個進程可以同時持有鎖的基本lockf通話保障,提供底層的文件系統支持鎖定。

我在Unix(FreeBSD)系統上試過了,它按預期工作。

+0

你的FreeBSD的lockf塊? – MCH

+0

@MCH好吧,我不能重現一個競爭條件,但我剛開始2個進程,在兩個文件中打開相同的文件(但是使用'O_RDWR'而不是'O_RDONLY'),並首先鎖定。當我嘗試在第二個鎖中取鎖時,它會被阻塞,直到第一個進程釋放鎖或關閉文件。一旦它獲得了鎖,它就會阻止任何其他嘗試採取它的進程。 –

-1

基於https://stackoverflow.com/a/7758075/5449837,我最終使用抽象的UNIX套接字而不是鎖定文件來同步進程(如果有人發佈了更好的答案,我會很樂意接受它)。

import contextlib as Contextlib 
import os as Os 
import socket as Socket 
import time as Time 


@Contextlib.contextmanager 
def locked(name, 
      timeout): 
    try: 
    lock = Socket.socket(Socket.AF_UNIX, 
         Socket.SOCK_DGRAM) 

    while True: 
     try: 
     lock.bind('\0' + name) 
     break 
     except: 
     if timeout: 
      Time.sleep(1) 
      timeout = timeout - 1 
     else: 
      raise 

    yield 
    except: 
    raise 
    finally: 
    lock.close() 


# usage: 
with locked("procedureName", 5): 
    pass # perform actions on shared resource 

向下投票者:小心解釋爲什麼你認爲這很糟糕?

+0

無關:1-請不要重命名stdlib模塊。另外,[即使對於你自己的模塊(在公共代碼中)遵循pep-8命名約定](https://www.python.org/dev/peps/pep-0008/#package-and-module-names)2 - 不要將問題放入答案中(這不是討論論壇)。如果你有問題, [問一個問題,而不是](http://stackoverflow.com/questions/ask) – jfs

+0

謝謝(將解決):) - 出於好奇,爲什麼不是1? – MCH

+0

爲什麼?因爲可讀性很重要。如果我看到'Socket';它看起來像一些自定義類。如果我看到'socket',我認爲它是一個stdlib模塊。沒有必要在沒有必要的情況下創建摩擦 – jfs