Python 多进程文件操作理解
目录
问题
多进程都对同一文件进行写入时,会因资源争夺导致文件内容错乱。
原因
- 操作系统的写入权限:同一时间只能有一个进程写入文件
- 随机调度:操作系统随机决定哪个进程执行,顺序无序
- 执行单元粒度:最小执行单元是原子级别(一行汇编代码),不是 Python 源代码行。一个进程可能写入几个字后就被切换到另一个进程
例如:10 个进程各写 10 个字。进程 1 写 3 个字就被切换,进程 3 写 2 个字又被切换到进程 5,最终文件内容混乱。
解决方案
方案 1:加锁
对写入操作加锁,保证一次只有一个进程完成完整的写入操作:
from multiprocessing import Lock
lock = Lock()
def write_file(lock, data):
with lock:
# 完整的写入操作
f = open('file.txt', 'a')
f.write(data)
f.close()
缺点:程序执行效率下降,且若写入操作分散在代码多处难以控制。
方案 2:回调函数(推荐)
将写入操作与计算逻辑分离,使用 multiprocessing.Pool 的回调函数:
import multiprocessing
def myCallback(x):
# 文件操作,写入 x 到文件
with open('file.txt', 'a') as f:
f.write(str(x) + '\n')
def getInfo(num):
# 返回需要写入的内容
return num * 2
pool = multiprocessing.Pool()
for i in range(10):
pool.apply_async(getInfo, (i,), callback=myCallback)
pool.close()
pool.join()
apply_async 用法
apply_async(func, args, callback=...):传递不定参数,非阻塞,支持结果返回后回调close():关闭 pool,使其不再接受新任务join():主进程阻塞等待子进程退出(必须在 close 后调用)
注意事项
pool.close() 必须在所有 apply_async 之后,不能放在循环内或 apply 前调用。错误示例会导致 AssertionError: assert self._state == RUN。
正确顺序:
pool = multiprocessing.Pool(processes=2)
result = []
for i in range(3):
msg = "hello %d" % i
result.append(pool.apply_async(func, [msg]))
pool.close()
pool.join()
print "Sub-process(es) done."
for obj in result:
print obj.get(timeout=1)