2009-01-28 114 views
105

我需要鎖定一個文件以用Python編寫。它將一次從多個Python進程訪問。我在網上找到了一些解決方案,但是大多數解決方案都失敗了,因爲它們通常只有基於Unix或Windows。在Python中鎖定文件

回答

5

鎖定文件通常是特定於平臺的操作,因此您可能需要考慮在不同操作系統上運行的可能性。例如:

import os 

def my_lock(f): 
    if os.name == "posix": 
     # Unix or OS X specific locking here 
    elif os.name == "nt": 
     # Windows specific locking here 
    else: 
     print "Unknown operating system, lock unavailable" 
+5

您可能已經知道這一點,但平臺模塊也可用於獲取有關運行平臺的信息。 platform.system()。 http://docs.python.org/library/platform.html。 「 – monkut 2009-01-29 00:54:14

8

協調對操作系統級別的單個文件的訪問充滿了各種您可能不想解決的問題。

你最好打賭的是有一個單獨的進程,協調對該文件的讀/寫訪問。

+15

」單獨的進程,座標讀取/寫入訪問該文件「 - 換句話說,實現數據庫服務器:-) – 2009-01-31 08:39:10

+1

這實際上是最好的答案。僅僅說「使用數據庫服務器」就過於簡單了,因爲db並不總是成爲正確的工具。如果它需要是純文本文件呢?一個好的解決方案可能是產生一個子進程,然後通過命名管道,unix套接字或共享內存訪問它。 – 2011-07-22 04:55:24

+7

-1因爲這只是FUD而沒有解釋。鎖定一個文件以供寫入對我來說似乎是一個非常簡單的概念,操作系統提供了像``flock`](http://linux.die.net/man/2/flock)這樣的函數。 「滾動自己的互斥體和守護進程來管理它們」似乎是一種相當極端和複雜的方法來解決......一個你實際上沒有告訴我們的問題,但只是存在一些可疑的暗示。 – 2016-05-10 11:38:28

30

有一個跨平臺的文件鎖定模塊的位置:Portalocker

雖然凱文說,寫從多個進程同時文件是你想避免,如果在所有可能的東西。

如果您可以將您的問題解決到數據庫中,則可以使用SQLite。它支持併發訪問並處理自己的鎖定。

+11

+1 - SQLite幾乎總是在這種情況下走的路。 – cdleary 2009-01-29 05:38:28

+2

Portalocker需要Windows的Python擴展。 – n611x007 2013-02-21 09:59:08

+1

@naxa有一個它只依賴於msvcrt和ctypes的變體,請參閱http://roundup.hg.sourceforge.net/hgweb/roundup/roundup/file/tip/roundup/backends/portalocker.py – 2013-04-15 21:21:40

9

鎖定是平臺和特定的設備,但通常,你有幾種選擇:

  1. 用羊羣(),或equivilent(如果您的操作系統支持)。這是建議性鎖定,除非您檢查鎖定,否則將其忽略。
  2. 使用鎖複製移動解鎖方法,您可以在其中複製文件,寫入新數據,然後移動它(移動,而不是複製 - 移動是Linux中的原子操作 - 檢查您的操作系統),而您檢查鎖定文件的存在。
  3. 使用一個目錄作爲「鎖」。如果你正在寫入NFS,這是必須的,因爲NFS不支持flock()。
  4. 也有可能在進程之間使用共享內存,但我從來沒有嘗試過;它非常特定於操作系統。

對於所有這些方法,您必須使用自旋鎖定(故障後重試)技術來獲取和測試鎖定。這確實給失步留下了一個小窗口,但它通常足夠小,不會成爲主要問題。

如果你正在尋找一個跨平臺的解決方案,那麼你最好通過其他機制登錄到另一個系統(其次最好的是上面的NFS技術)。

請注意,sqlite受到與普通文件相同的NFS約束,因此您無法寫入網絡共享上的sqlite數據庫並獲得免費的同步。

1

我發現了一個簡單的工作(!)來自grizzled-python的implementation

簡單使用os.open(...,O_EXCL)+ os.close()在windows上不起作用。

1

我一直在努力處理這種情況,我在同一目錄/文件夾中運行同一程序的多個副本並記錄錯誤。我的方法是在打開日誌文件之前寫入「鎖定文件」到光盤。程序在繼續之前檢查是否存在「鎖定文件」,如果存在「鎖定文件」,則等待它。

這裏是代碼:「鎖定文件」

def errlogger(error): 

    while True: 
     if not exists('errloglock'): 
      lock = open('errloglock', 'w') 
      if exists('errorlog'): log = open('errorlog', 'a') 
      else: log = open('errorlog', 'w') 
      log.write(str(datetime.utcnow())[0:-7] + ' ' + error + '\n') 
      log.close() 
      remove('errloglock') 
      return 
     else: 
      check = stat('errloglock') 
      if time() - check.st_ctime > 0.01: remove('errloglock') 
      print('waiting my turn') 

編輯--- 思考過一些關於陳舊的鎖的意見,上述我編輯的代碼中添加一個檢查爲的陳舊程度後時序我的系統上這個函數的幾個萬次都給並從之前平均0.002066 ...秒:

lock = open('errloglock', 'w') 

只是後:

remove('errloglock') 

所以我想我會用5次啓動這個數字表明過時並監測問題的情況。

而且,我是用時間的工作,我意識到,我有一些代碼,這是不是真的有必要:

lock.close() 

,我曾緊隨公開聲明,所以我已刪除了它在這個編輯中。

5

我一直在尋找多種解決方案做到這一點,我選擇一直 oslo.concurrency

它的強大和相對有據可查。它基於緊固件。

其他的解決方案:

0

最新更新你會發現pylocker非常有用。它可以用來鎖定一個文件或鎖定機制,可以一次從多個Python進程訪問。

如果您只是想鎖定文件,這裏是它的工作原理是:

import uuid 
from pylocker import Locker 

# create a unique lock pass. This can be any string. 
lpass = str(uuid.uuid1()) 

# create locker instance. 
FL = Locker(filePath='myfile.txt', lockPass=lpass, mode='w') 

# aquire the lock 
with FL as r: 
    # get the result 
    acquired, code, fd = r 

    # check if aquired. 
    if fd is not None: 
     print fd 
     fd.write("I have succesfuly aquired the lock !") 

# no need to release anything or to close the file descriptor, 
# with statement takes care of that. let's print fd and verify that. 
print fd 
1

場景是這樣的: 用戶請求的文件做一些事情。然後,如果用戶再次發送相同的請求,它會通知用戶第二個請求沒有完成,直到第一個請求結束。這就是爲什麼我使用鎖定機制來處理這個問題。

這是我工作的代碼:

from lockfile import LockFile 
lock = LockFile(lock_file_path) 
status = "" 
if not lock.is_locked(): 
    lock.acquire() 
    status = lock.path + ' is locked.' 
    print status 
else: 
    status = lock.path + " is already locked." 
    print status 

return status 
2

其他的解決方案列舉了大量的外部代碼庫。如果您更願意自己動手​​,那麼下面是一些跨平臺解決方案的代碼,它使用Linux/DOS系統上各自的文件鎖定工具。

try: 
    # Posix based file locking (Linux, Ubuntu, MacOS, etc.) 
    import fcntl 
    def lock_file(f): 
     fcntl.lockf(f, fcntl.LOCK_EX) 
    def unlock_file(f): pass 
except ModuleNotFoundError: 
    # Windows file locking 
    import msvcrt 
    def file_size(f): 
     return os.path.getsize(os.path.realpath(f.name)) 
    def lock_file(f): 
     msvcrt.locking(f.fileno(), msvcrt.LK_RLCK, file_size(f)) 
    def unlock_file(f): 
     msvcrt.locking(f.fileno(), msvcrt.LK_UNLCK, file_size(f)) 


# Class for ensuring that all file operations are atomic, treat 
# initialization like a standard call to 'open' that happens to be atomic 
class AtomicOpen: 
    # Open the file with arguments provided by user. Then acquire 
    # a lock on that file object (WARNING: Advisory locking) 
    def __init__(self, path, *args, **kwargs): 
     # Open the file and acquire a lock on the file before operating 
     self.file = open(path,*args, **kwargs) 
     # Lock the opened file 
     lock_file(self.file) 

    # Return the opened file object (knowing a lock has been obtained) 
    def __enter__(self, *args, **kwargs): return self.file 

    # Allows users to use the 'close' function if they want, in case 
    # the user did not have the AtomicOpen in a "with" block. 
    def close(self): self.__exit__() 

    # Unlock the file and close the file object 
    def __exit__(self, exc_type=None, exc_value=None, traceback=None):   
     # Release the lock on the file 
     unlock_file(self.file) 
     self.file.close() 
     # Handle exceptions that may have come up during execution, by 
     # default any exceptions are raised to the user 
     if (exc_type != None): return False 
     else:     return True   

現在,「AtomicOpen」可用於任何通常使用「open」語句的地方。

警告:如果在Windows和Python上運行退出之前崩潰被調用,我不確定鎖定行爲是什麼。

警告:此處提供的鎖定是建議性的,不是絕對的。所有潛在的競爭進程必須使用「AtomicOpen」類。