Python - fcntl.flock(fd, op) 檔案鎖應用範例
Posted on May 8, 2018 in Python 程式設計 - 高階 by Amo Chen ‐ 2 min read
談 flock 之前,先來聊聊為什麼會需要 flock 。
如果有多個 Processes 同時對同 1 個檔案進行讀寫操作,很容易就會發生同步問題,這時候最簡單的解決的方法就是用個鎖( Lock
)確保同一時間只有 1 個 Process 可以對 1 個共用的檔案進行操作。
而 flock 就是幫忙建立 Lock 的一個機制,其原理是利用 1 個檔案作為鎖(Lock) , Process 要對某個檔案進行讀寫操作時,得先獲得鎖(Lock) 才能進行讀寫操作,其他沒獲得鎖(Lock)的 Process 則會被阻擋無法執行。
另一種常見的情況是為了確保 Crontab 內定期執行的程式同一時間只會有 1 個 Process 在執行,也會利用 flock 避免因為下一次定期執行時間到而又執行一個新的 Process 。
flock 除了可以用 flock
指令之外, Python 也內建提供 fcntl 模組幫我們建立/獲取 flock 鎖。
範例
以下是利用 fcntl.flock(fd, op)
建立 flock 的範例,可以看到主要是 fcntl.flock(f, fcntl.LOCK_EX)
中的參數 fcntl.LOCK_EX | fcntl.LOCK_NB
建立一個同一時間只有 1 個 Process 能夠操作的鎖,其中 fcntl.LOCK_EX
代表建立 exclusive lock
確保同一時間只有 1 個 Process 可以獲取鎖(Lock),而 fcntl.LOCK_NB
則是代表其他沒獲取到鎖(Lock)則不要一直等待獲取到鎖,而是直接 raise OSError
。
import fcntl
from time import sleep
lockfile = '/tmp/fcntl.lock'
try:
f = open(lockfile, 'r')
except IOError:
f = open(lockfile, 'wb')
try:
fcntl.flock(f, fcntl.LOCK_EX | fcntl.LOCK_NB)
sleep(600)
except OSError:
print('locked')
finally:
fcntl.flock(f, fcntl.LOCK_UN)
上述範例執行之後會 sleep 10 分鐘,如果這時候有其他的程式想要同樣想要透過 fcntl.flock(f, fcntl.LOCK_EX | fcntl.LOCK_NB)
獲取鎖就會失敗而列印出 locked (可以複製上述程式到新的檔案並執行,即可看到效果)。
最後經過 10 分鐘之後或者有任何 IOError 以外的 Exception 的話則會進到 finally 區塊中, fcntl.flock(f, fcntl.LOCK_UN)
就會將鎖釋出,避免下次執行時遇到一直遲遲沒釋放的鎖導致程式無法執行。
將 flock 包裝成 decorator
因為 flock 的使用也不算罕見,所以可以將 flock 包裝成 decorator ,例如以下的 exclusive_lock decorator 就是確保某個 function 同一時間只能有一個 Process 在執行:
import os
import fcntl
from time import sleep
def exclusive_lock(fn):
lockfile = os.path.join('/tmp/', 'ex_{}.lock'.format(fn.__name__))
def wrapped_func(*args, **kwargs):
try:
fd = open(lockfile, 'r')
except IOError:
fd = open(lockfile, 'wb')
try:
fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
fn(*args, **kwargs)
except IOError:
pass
finally:
fcntl.flock(fd, fcntl.LOCK_UN)
return wrapped_func
@exclusive_lock
def only_once_process_can_sleep():
sleep(100)
if __name__ == '__main__':
only_once_process_can_sleep()
上述程式可以開多個 Terminal 執行看看,不管多少個 Terminal 執行該程式,同時間就只會有 1 個 Process 進入 sleep 。
flock 該注意的事
Linux 上的 Lock 有 advisory lock & mandatory lock 的區別,advisory lock 指的是要 Process 主動去獲取鎖才能夠作用,而 mandatory lock 是作業系統提供的鎖,不需要 Process 自己去獲取就會起作用。而 flock 屬於 advisory lock ,意思是假設 A 與 B 2 個 Processes ,假設 A 獲取了鎖(Lock)情況下 ,如果要確保同一時間只有一個 Process 可獲取鎖,那麼 B 在執行時也必須主動去獲取鎖,如果 B 忘記去獲取鎖,將導致 A B 都在同一時間可以執行,使得該鎖沒有起任何作用。
References
https://docs.python.org/3/library/fcntl.html
https://linux.die.net/man/2/flock