1.遇到的问题:
之前,因为考虑到Python多进程可以充分利用CPU核数,提高程序的效率,所以就使用多进程写文件。但是向同一个文件写入数据的时候,由于多进程是并发进行,操作系统中会不清楚到底要写入哪个数据到文件中,所以会出现资源竞争混乱,导致文件内容轮乱。
不过你还可以给文件加锁,但是加锁一般会造成程序的执行速度下降,而且如果进程在多处需要向文件输出,也不好把这些代码整个都锁起来,如果都锁起来,那跟单进程还有什么区别。
2.解决思路如下:
跟把文件输出集中在一起也差不多,就是把进程需要写入文件的内容作为返回值返回给回调函数,使用回调函数向文件中写入内容。不过还有一种更加优雅的解决方式:使用multiprocessing库的回调函数功能。
3.应用场景:
进程池中任何一个任务一旦处理完了,就立即告知主进程:我好了,你可以处理我的结果了。主进程则调用一个函数去处理该结果,该函数即回调函数。我们可以把耗时间(阻塞)的任务放到进程池中,然后指定回调函数(主进程负责执行),这样主进程在执行回调函数时就省去了I/O的过程,直接拿到的是任务的结果。
4.异步回调函数 apply_async
源代码如下,有兴趣可以看看:
def apply_async(self, func, args=(), kwds={}, callback=None,
error_callback=None):
'''
Asynchronous version of `apply()` method.
'''
if self._state != RUN:
raise ValueError("Pool not running")
result = ApplyResult(self._cache, callback, error_callback)
self._taskqueue.put(([(result._job, 0, func, args, kwds)], None))
return result
参数解释下:
- func 是调用的函数
- args 是给指定的函数传递的参数,以元组的方式传递
- kwargs:传递字典类型参数
- callback:只能接受单个参数的可调用对象,当结果完成时就找对象的callback回调函数
- error_callback:只能一个接受单个参数的可调用对象。如果目标函数失败,则以该异常实例调用error_callback。
代码示例:
import multiprocessing
# 设置回调函数
def setcallback(x):
with open('result.txt', 'a+') as f:
line = str(x) + "\n"
f.write(line)
def multiplication(num):
return num*(num+1)
if __name__ == '__main__':
pool = multiprocessing.Pool(6)
for i in range(1000):
pool.apply_async(func=multiplication, args=(i,), callback=setcallback)
pool.close()
pool.join()
这是保存完毕的文件,部分内容,可以看出数据没有混乱。
总结:
apply_async是异步非阻塞式,不用等待当前进程执行完毕,随时跟进操作系统调度来进行进程切换,即多个进程并行执行,提高程序的执行效率。回调应该立即完成,否则处理结果的线程将被阻塞。