Python多线程文件读写加锁机制详解:fcntl与threading.Lock实战
本文详解Python多线程环境下文件读写的加锁方案。通过fcntl模块的flock函数实现系统级文件锁,支持共享与排他模式;同时利用threading模块的Lock类实现进程内互斥锁。文章提供完整代码示例,解析锁状态转换及阻塞机制,确保多线程并发写入数据的安全性。
Python在处理I/O密集型任务时,多线程相比单线程具有明显优势。然而,在多线程开发场景中,经常涉及对同一文件的读写操作。当多个线程同时尝试访问同一文件时,必须引入文件锁机制来协调访问,防止数据冲突或损坏。
使用fcntl模块
在Linux系统中,Python标准库提供了现成的文件锁功能,主要依赖于fcntl模块。该模块封装了Unix系统下的fcntl()和ioctl()系统调用接口,为开发者提供了底层文件控制能力。
实现文件锁的核心函数是fcntl.flock(fd, operation),其中fd为文件描述符,operation指定具体的锁操作类型。
参数operation用于指定锁的操作类型,其可选值包括以下几种情况:
LOCK_SH:表示创建共享锁。在任意时刻,一个文件可以被多个进程同时持有共享锁,适用于只读并发场景。
LOCK_EX:表示创建排他锁。在任意时刻,一个文件只能被一个进程持有排他锁,适用于写入等需要独占资源的场景。
LOCK_UN:表示释放或删除当前进程持有的锁,解除对文件的锁定状态。
LOCK_MAND:主要用于共享模式下的强制锁。它可以与LOCK_READ或LOCK_WRITE联合使用,以控制是否允许并发读操作或并发写操作,适用于更复杂的访问控制需求。
代码示例
import fcntl
import threading
import time
def writetoTxt(txtFile):
id = threading.currentThread().getName()
with open(txtFile, 'a') as f:
fcntl.flock(f.fileno(), fcntl.LOCK_EX) #加锁
print "{0} acquire lock".format(id)
f.write("write from {0} \r\n".format(id))
time.sleep(3)
# 在with块外,文件关闭,自动解锁
print "{0} exit".format(id)
for i in range(5):
myThread = threading.Thread(target=writetoTxt, args=("test.txt",))
myThread.start()
运行上述代码时,控制台会依次打印哪个线程成功获取了锁,并随后对文件执行读写操作。
Thread-1 acquire lock Thread-1 exit Thread-2 acquire lock Thread-2 exit Thread-3 acquire lock Thread-3 exit Thread-5 acquire lock Thread-5 exit Thread-4 acquire lock Thread-4 exit
小结
通过调用
fcntl.flock(f.fileno(), fcntl.LOCK_EX)
对目标文件进行加锁。如果此时有其他线程尝试对test文件加锁,该线程将被阻塞,直到锁被释放。
当线程执行完毕退出with代码块时,文件关闭,锁会自动释放。此外,也可以采取主动方式解锁:调用
fcntl.flock(f.fileno(),fcntl.LOCK_UN)
函数,手动对文件test进行解锁操作。
使用线程锁(Lock)
当多个线程共享同一数据资源时,必须进行同步控制,否则可能导致不可预期的结果,即出现“线程不安全”现象。
线程同步能够保证多个线程安全访问竞争资源,最简单的同步机制是引入互斥锁。 互斥锁为资源引入一个状态:锁定/非锁定。 某个线程要更改共享数据时,先将其锁定,此时资源的状态为“锁定”,其他线程不能更改; 直到该线程释放资源,将资源的状态变成“非锁定”,其他的线程才能再次锁定该资源。 互斥锁保证了每次只有一个线程进行写入操作,从而保证了多线程情况下数据的正确性。
Python的threading模块中定义了Lock类,可以方便地处理线程间的锁定问题:
#创建锁 mutex = threading.Lock() #锁定 mutex.acquire([timeout]) #解锁 mutex.release()
代码示例
使用互斥锁实现上述文件读写例子的代码如下:
import threading
import time
def writetoTxt(txtFile):
id = threading.currentThread().getName()
mutex.acquire(10)
with open(txtFile, 'a') as f:
print "Thread {0} acquire lock".format(id)
f.write("write from thread {0} \r\n".format(id))
time.sleep(3)
mutex.release()
print "Thread {0} exit".format(id)
mutex = threading.Lock()
for i in range(5):
myThread = threading.Thread(target=writetoTxt, args=("test.txt",))
myThread.start()
需要注意的是,上述代码本质上表现为顺序执行的单线程行为,因为互斥锁强制了串行访问。
运行结果:
Thread Thread-1 acquire lock Thread Thread-1 exit Thread Thread-2 acquire lock Thread Thread-2 exit Thread Thread-3 acquire lock Thread Thread-3 exit Thread Thread-4 acquire lock Thread Thread-4 exit Thread Thread-5 acquire lock Thread Thread-5 exit
小结
当一个线程调用锁的acquire()方法成功获得锁时,锁的状态变为“locked”。此时每次仅有一个线程能持有该锁。如果其他线程试图获取此锁,它们将进入“blocked”状态,这被称为“同步阻塞”。
直到持有锁的线程调用release()方法释放锁后,锁状态变为“unlocked”。线程调度程序会从处于同步阻塞状态的线程队列中选择一个线程获得锁,并将其切换为运行(running)状态。
以上关于Python多线程读写文件加锁的实例详解内容到此结束,希望能为读者提供有价值的参考。欢迎继续支持本站获取更多技术资讯。
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









