施工中...
一. multiprocessing.Pool相关补遗
1. map:
在使用进程池的时候,我们一般使用的是apply和apply_async方法来申请进程执行任务;其实还有另外一系列的方法map用于在对一个序列里的元素,
进行相同的函数调用时使用Pool来实现并发。其使用的方法和內建函数map()非常的相似,只不过变成了用线程池来调用,示例如下:
1 import multiprocessing 2 3 def func1(x): 4 print (x * x) 5 6 if __name__ == '__main__': 7 pool = multiprocessing.Pool(multiprocessing.cpu_count()) 8 i_list = range(8) 9 pool.map(func1, i_list)
运行结果:
我们可以看到结果符合预期,把序列中每个元素都使用func1函数处理了;此外我们也能在运行时候发现结果是一个个打印的,说明map是阻塞执行的,类似apply方法,所以与之相对的,map也有非阻塞的版本。
2. map_async方法
map_async方法就是map方法的非阻塞版本,用法上也和apply_async很像,需要在close进程池以后再join来等待进程都结束;此外也能接受回调函数,示例如下:
1 def func1(x): 2 return x * x 3 4 def func2(arg): 5 print(arg) 6 7 if __name__ == '__main__': 8 pool = multiprocessing.Pool(4) 9 i_list = range(8) 10 pool.map_async(func1, i_list, callback=func2) 11 pool.close() 12 pool.join()
结果如下:
所以我们可以发现map_async和apply_async的区别在于其回调函数的入参是整个序列运行后的新序列的结果,而非单个元素运行函数后的结果。此外我们也可以对map_async的返回值MapResult的实例
调用get()方法来获得异步运行的结果。这也类似apply_async返回的ApplyResult实例(其实MapResult是ApplyResult的子类),示例如下:
1 def func1(x): 2 return x * x 3 4 def func2(arg): 5 print(arg) 6 7 if __name__ == '__main__': 8 pool = multiprocessing.Pool(4) 9 i_list = range(8) 10 rst = pool.map_async(func1, i_list, callback=func2) 11 pool.close() 12 pool.join() 13 print(rst.get())
结果如下:
我们同样可以发现,MapResult的示例get到的结果本身就是一个完整的对每个原序列的元素处理后的新序列了,与apply_async调用get后不同。
3. imap 方法
此外还有imap方法,其行为与map方法非常类似,不过返回的是一个可迭代对象,所以要通过迭代来获得结果,示例如下:
1 def func1(x): 2 return x * x 3 4 if __name__ == '__main__': 5 pool = multiprocessing.Pool(4) 6 i_list = range(8) 7 for i in pool.imap(func1, i_list): 8 print(i)
结果与map一样,就不展示了。
4. starmap和starmap_async方法
starmap方法和map方法用法类似,只是把序列中的每个元素按照变长参数解包后(即 *args)的形式传递给方法。starmap_async是starmap的异步版本。使用方法就不展示了...
5. multiprocessing.cpu_count()
这个方法能够获得当前电脑CPU的核数,我们知道一般来说多进程并发的话,设置并发个数为CPU核数或者其倍数为好,这个方法可以帮助我们。其实如果在调用multiprocessing.Pool()创建实例的时候,
如果不传入参,在Pool的初始化过程中也会调用本方法来设置一个默认的并发进程数。
6. 异步调用结果的get方法
在上面讲map_async的时候已经提到了这个方法。因为我们知道异步调用并发的话,当时是无法获得结果的,但是会返回给我们一个ApplyResult类的实例(以apply_async方法举例),在对pool使用join方法
后,确定所有子进程都已经结束以后,就可以使用本方法来逐个获取结果。另外apply方法,其实就是在内部直接对apply_async的结果进行调用get方法来实现的。实例如下:
1 def func1(x): 2 return x * x 3 4 def func2(arg): 5 print(arg) 6 7 if __name__ == '__main__': 8 9 pool = multiprocessing.Pool(4) 10 i_list = range(8) 11 rst = [] 12 for i in range(8): 13 rst.append(pool.apply_async(func1, args=(i,), callback=func2)) 14 pool.close() 15 pool.join() 16 print([i.get() for i in rst])
结果如下:
7. 使用with来调用Pool
在看multiprocessing的pool类的代码的时候,看到它还实现了__enter__和 __exit__,所以可以用with来调用。
__enter__方法里返回的是self,所以可以as成另一个变量在with内部进行操作,此外在__exit__调用了pool的terminate方法,能够确保退出,后面不用自己操心,示例如下:
1 def func_callbak(arg): 2 print(arg) 3 4 def func(msg): 5 time.sleep(0.5) 6 return msg 7 8 def func_join(msg): 9 time.sleep(0.5) 10 print(msg) 11 12 with Pool(processes=3) as pool: 13 for i in range(6): 14 msg = "hello %d" % i 15 pool.apply_async(func, (msg, ), callback=func_callbak) 16 pool.close() 17 pool.join() 18 19 with Pool(processes=3) as pool: 20 for i in range(6): 21 msg = "hello %d" % i 22 pool.apply(func_join, (msg, )) 23 pool.close()
结果与普通的调用法师一直的,就不再展示。
二. 扩展:第三方库deco
关于多进程的使用,我发现过一个有趣的第三方库,对于简单的多进程,我们可以用装饰器来实现调用,非常方便,介绍给大家~
安装方法:
pip install deco
使用方法:
对想要进行并发的方法,加上@concurrent修饰。对于调用使用@concurrent修饰的函数的函数,加上@synchronized修饰自己。修饰第二个函数是可选的。
1 @concurrent # We add this for the concurrent function 2 def process_lat_lon(lat, lon, data): 3 #Does some work which takes a while 4 return result 5 6 @synchronized # And we add this for the function which calls the concurrent function 7 def process_data_set(data): 8 results = defaultdict(dict) 9 for lat in range(...): 10 for lon in range(...): 11 results[lat][lon] = process_lat_lon(lat, lon, data) 12 return results
其实@concurrent修饰器只是使用multiprocessing.pool并行化了目标函数的调用,包装了一下。不过它自动处理把函数参数的变化转变成了基于索引的形式,pool模块本来是没有这个效果的。
@synchronized修饰器自动插入同步事件。
附个连接,非常详细介绍多进程和线程的一篇博客:http://www.cnblogs.com/resn/p/5591419.html