并发在很多有时效性任务的场景中是有很高的要求的,一个好的并发处理策略能够很大程度上缓解系统的压力,提升资源的使用率,程序并发的实现方式主要有两种:多线程并发和多进程并发。在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,同样是采用异步并发执行的方式,是非阻塞的方式,多个进程间并行执行,不对原始任务进行子任务的划分,一个进程结束后另一个进程自动就会加到当前的进程池中。

       对结果分析我们发现:第二种方式耗时>第一种方式耗时>第三种方式耗时,这其实也对应了我们上段的分析,同步执行方式最慢,因为是串行执行的;第一种异步方式由于划分了子任务,单个子任务内并发执行,不同子任务间串行执行,这个时间取决于子任务内最晚执行结束的任务单元的耗时;第三种方式直接对原始任务进行并发处理,耗时最短,不同任务间都是并发执行的。