并发在很多有时效性任务的场景中是有很高的要求的,一个好的并发处理策略能够很大程度上缓解系统的压力,提升资源的使用率,程序并发的实现方式主要有两种:多线程并发和多进程并发。在python中由于GIL(全局解释器锁)的存在,导致在同一时刻中只有一个线程能够获取系统的计算资源,所以在python中的线程实际上没有并发的能力,如果想要借助CPU多核的计算能力来进行程序的并发就需要使用到多进程了,在python中多进程已经被封装在multiprocessing模块中了。
今天主要是基于multiprocessing模块来进行一些并发的实践,具体实践内容如下:
#!usr/bin/env python
#encoding:utf-8
from __future__ import division
'''
__Author__:沂水寒城
功能: python多进程实践
'''
import time
import multiprocessing
def cutList(one_list,c=3):
'''
将一个长度为n的列表划分 ,每个子列表中包含m个元素
'''
return [one_list[i:i+c] for i in range(len(one_list)) if i%c==0]
def executeFunc(num):
'''
函数执行体
'''
res=0
for i in range(num):
res+=i
print "res: ", res
if __name__ == "__main__":
num_list=[123,456,78,90,134,1657,12,4324,768,1324,546,23,24243,126,908,323,4356,54678,345,235,897,
234786,7786,2389789,234234,436347,231526,12365,64,12351,667,32656832,8908456,4356236]
task_list=cutList(num_list,c=4)
#异步执行(非阻塞)
start=time.time()
while task_list:
one_task=task_list.pop(0)
pool=multiprocessing.Pool(processes=4)
for i in range(len(one_task)):
pool.apply_async(executeFunc,(one_task[i],))
pool.close()
pool.join()
print "Sub-process(es) done."
end=time.time()
print 'time_consume: ',end-start
print '=*='*30
#同步执行(阻塞)
start=time.time()
while task_list:
one_task=task_list.pop(0)
pool=multiprocessing.Pool(processes=4)
for i in range(len(one_task)):
pool.apply(executeFunc,(one_task[i],))
pool.close()
pool.join()
print "Sub-process(es) done."
end=time.time()
print 'time_consume: ',end-start
print '=*='*30
#异步执行(非阻塞)
start=time.time()
pool=multiprocessing.Pool(processes=4)
for i in range(len(num_list)):
pool.apply_async(executeFunc,(num_list[i],))
pool.close()
pool.join()
print "Sub-process(es) done."
end=time.time()
print 'time_consume: ',end-start
print '=*='*30
上述运行结果如下:
res: 7503
res: 103740
res: 3003
res: 4005
res: 8911
res: 1371996
res: 66
res: 9346326
res: 294528
res: 875826
res: 148785
res: 253
res: 293849403
res: 7875
res: 411778
res: 52003
res: 9485190
res: 1494814503
res: 59340
res: 27495
res: 401856
res: 27562115505
res: 30307005
res: 2855544537366
res: 27432666261
res: 76440430
res: 26802028575
res: 95199134031
res: 2016
res: 76267425
res: 222111
res: 533234321809696
res: 9488393865730
res: 39680289697740
Sub-process(es) done.
time_consume: 4.99900007248
=*==*==*==*==*==*==*==*==*==*==*==*==*==*==*==*==*==*==*==*==*==*==*==*==*==*==*==*==*==*=
res: 7503
res: 3003
res: 103740
res: 4005
res: 8911
res: 1371996
res: 66
res: 9346326
res: 294528
res: 875826
res: 148785
res: 253
res: 293849403
res: 7875
res: 411778
res: 52003
res: 9485190
res: 1494814503
res: 59340
res: 27495
res: 401856
res: 27562115505
res: 30307005
res: 2855544537366
res: 95199134031
res: 27432666261
res: 26802028575
res: 76440430
res: 2016
res: 76267425
res: 222111
res: 533234321809696
res: 39680289697740
res: 9488393865730
Sub-process(es) done.
time_consume: 5.2650001049
=*==*==*==*==*==*==*==*==*==*==*==*==*==*==*==*==*==*==*==*==*==*==*==*==*==*==*==*==*==*=
res: 30307005
res: 2855544537366
res: 95199134031
res: 9488393865730
res: 39680289697740
res: 7503
res: 103740
res: 3003
res: 4005
res: 8911
res: 1371996
res: 66
res: 9346326
res: 294528
res: 875826
res: 148785
res: 253
res: 293849403
res: 7875
res: 411778
res: 52003
res: 9485190
res: 1494814503
res: 59340
res: 27495
res: 401856
res: 27562115505
res: 27432666261
res: 26802028575
res: 76440430
res: 2016
res: 76267425
res: 222111
res: 533234321809696
Sub-process(es) done.
time_consume: 2.91799998283
=*==*==*==*==*==*==*==*==*==*==*==*==*==*==*==*==*==*==*==*==*==*==*==*==*==*==*==*==*==*=
我们的程序体中计算的是一个累加求和的操作,由于并发性的原因导致结果的顺序不尽相同,这里我们不再关注结果的数值,而是重点关注结果的运行时间。从上述结果中提取出来每种方式执行耗时情况如下:
time_consume: 4.99900007248
time_consume: 5.2650001049
time_consume: 2.91799998283
我们对上述运行时间做一点分析,第一种方式耗时:4.99900007248s,这是采用异步并发执行的方式,是非阻塞的方式,多个进程间并行执行。但是实现划分了子任务,每个子任务结束后下一个子任务才会执行;第二种方式耗时:5.2650001049s,这是采用同步并发执行的方式,是阻塞的,也就是说不同进程间是串行执行的;最后一种方式耗时:2.91799998283,同样是采用异步并发执行的方式,是非阻塞的方式,多个进程间并行执行,不对原始任务进行子任务的划分,一个进程结束后另一个进程自动就会加到当前的进程池中。
对结果分析我们发现:第二种方式耗时>第一种方式耗时>第三种方式耗时,这其实也对应了我们上段的分析,同步执行方式最慢,因为是串行执行的;第一种异步方式由于划分了子任务,单个子任务内并发执行,不同子任务间串行执行,这个时间取决于子任务内最晚执行结束的任务单元的耗时;第三种方式直接对原始任务进行并发处理,耗时最短,不同任务间都是并发执行的。