Python多进程文件写入锁机制详解与常见陷阱
本文详细解析Python中使用multiprocessing.Lock进行多进程文件写入时的常见误区。通过对比直接传锁、进程池传锁及每次打开文件三种方案,指出进程间内存不共享导致锁失效的问题,并给出在大数据量写入场景下的有效实践与注意事项。
多进程文件写入锁失效问题分析
在多进程并发场景下,若多个进程同时向同一文件执行写入操作且未加锁控制,极易引发数据交错或错乱。
为解决此问题,Python标准库提供了multiprocessing.Lock()用于实现进程间的互斥访问。
初始尝试中,笔者采用了以下代码结构:
import multiprocessing
lock = multiprocessing.Lock()
class MatchProcess(multiprocessing.Process):
def __init__(self, threadId, mfile, lock):
multiprocessing.Process.__init__(self)
self.threadId = threadId
self.mfile = mfile
self.lock = lock
def run(self):
while True:
self.lock.acquire()
try:
self.mfile.write('111111111111111111' + '\n')
finally:
self.lock.release()
if __name__ == '__main__':
mf = open('test.lst', 'w')
for i in range(15):
p = MatchProcess(i, mf, lock)
p.start()
实际运行结果显示,文件内容依然出现错乱(注:示例中写入的“1111”仅为演示,实际代码逻辑不同)。
分析表明,尽管lock对象作为参数传递给了各个子进程,但由于进程间内存空间相互独立,每个进程持有的实际上是锁的不同副本,因此无法实现跨进程的互斥效果。
进程池与锁机制的兼容性测试
随后尝试使用进程池(Pool)是否可行,具体测试代码如下:
def run(line):
lock.acquire()
try:
mfile.write('111111111111111111' + '\n')
finally:
lock.release()
sf = open('test.lst', 'r')
data_lst = list()
for line in sf:
line = line.strip()
data_lst.append(line)
pool = Pool(15)
pool.map_async(run, data_lst) #map_async方法会将data_lst这个可迭代的对象里面的每个元素依次传入run方法来执行
pool.close()
pool.join()
print 'over'
需特别注意以下代码片段:
pool.close()
pool.join()
这两行代码不可或缺,若缺失,主进程执行完毕后可能直接退出,导致尚未完成的子进程被强制终止。
因此,只有当所有子进程执行完毕后,程序才会打印“over”并结束。
然而测试发现,即便使用进程池,锁机制依然未能起到预期的保护作用。
有效解决方案与性能权衡
最终采用的有效方案如下:
def run(line):
mfile = open('test2.lst', 'a')
lock.acquire()
try:
mfile.write('111111111111111111' + '\n')
finally:
lock.release()
sf = open('test.lst', 'r')
data_lst = list()
for line in sf:
line = line.strip()
data_lst.append(line)
pList = []
for line in line_lst:
p = multiprocessing.Process(target=run, args=(line, lock))
p.start()
pList.append(p)
for p in pList:
p.join()
经实测,该方式下锁机制确实生效。但在处理大量数据写入时,由于频繁加锁解锁,执行速度显著变慢。
此外,一个令人困惑的现象是:若尝试在创建Process对象时,通过args参数将已打开的文件对象传入run方法,数据无法成功写入文件。具体原因尚待进一步探究。
受限于上述问题,只能采用前文提到的“笨办法”,即每次写入时重新打开文件、写入后关闭。这显然并非最优解,若有更优方案,欢迎留言交流。
由此可见,将已打开的文件句柄传入子进程是无效的。可行的替代思路是仅传入文件名,在run方法中每次写入前打开文件,写入完成后立即关闭。
这里存在一个疑问:为何前文提到的第一种方案(文件打开后传入)在特定情况下看似可行,而此处却无效?
以上关于Python多进程锁的使用方法及常见陷阱的分享,旨在为读者提供参考,欢迎指正与讨论。
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













