Bootstrap

Python fcntl文件锁实践

fcntl文件锁实践

官方文档 https://docs.python.org/3.8/library/fcntl.html

基本使用

import fcntl

file_path = 'file2lock.txt'

def test_lock(mode, lock):
    f = open(file_path, mode)
    fcntl.flock(f, lock) # 对文件加锁
    # fcntl.flock(f.fileno(), lock)  # 第一个参数传f.fileno()也一样
    # file operation
    fcntl.flock(f, fcntl.LOCK_UN)  # 解锁
    f.close()

if __name__ == '__main__':
    test_lock('a', fcntl.LOCK_EX)

LOCK_EX 互斥锁
LOCK_SH 共享锁
LOCK_UN 解锁
LOCK_NB 非阻塞锁,须与LOCK_EX或LOCK_SH按位与使用
即共有以下几种:
LOCK_EX,LOCK_SH,LOCK_UN,LOCK_EX|LOCK_NB,LOCK_SH|LOCK_NB

劝告锁

fcntl只是劝告锁,并非对文件真正上锁,因此使用时须在代码中显式获取fcntl锁
验证:

# test_lock.py
import fcntl

file_path = 'file2lock.txt'

def test_lock(mode, lock):
    f = open(file_path, mode)
    print('try lock')
    fcntl.flock(f, lock)
    i = input('Enter: ')
    fcntl.flock(f, fcntl.LOCK_UN)
    print('file unlocked')
    f.close()

if __name__ == '__main__':
    test_lock('a', fcntl.LOCK_SH)
# file_operation.py
file_path = 'file2lock.txt'
with open(file_path, 'a') as f:
    f.write(f"\nwrite without lock")

先运行test_lock.py进程上锁后卡在等待输入,再运行file_operation.py可直接写文件;另起一个进程运行test_lock.py,进程卡在获取锁。

进程退出自动解锁

验证:
还是两个进程都运行test_lock.py,一个进程等待输入一个进程卡在获取锁,杀掉等待输入进程,另外一个进程自动获取到锁。

自锁自解

fcntl上锁后,只能由上锁线程解锁。

进程验证

# test_lock_1.py
def test_lock(mode, lock):
    f = open(file_path, mode)
    fcntl.flock(f, fcntl.LOCK_UN) # 先尝试解锁
    print('try lock')
    fcntl.flock(f, lock)
    i = input('Enter: ')
    fcntl.flock(f, fcntl.LOCK_UN)
    print('file unlocked')
    f.close()

先执行test_lock.py再运行test_lock_1.py,后者仍然卡在获取锁,因此解锁必须同进程。

线程验证

# test_lock_2.py
def thread_unlock():
    time.sleep(5)
    f = open(file_path, 'a')
    fcntl.flock(f, fcntl.LOCK_UN)
    print('subthread unlocked')

def test_lock(mode, lock):
    print(os.getpid())
    f = open(file_path, mode)
    print('try lock')
    fcntl.flock(f, lock)
    p = threading.Thread(target=thread_unlock)
    p.start()
    i = input('Enter: ')
    fcntl.flock(f, fcntl.LOCK_UN)
    print('file unlocked')
    f.close()

先运行test_lock_2.py再运行test_lock.py,前者子线程解锁后后者仍然卡在获取锁,因子解锁必须同线程。

;