目的

在Python开发中流传着这样一句话:人生苦短,我用Python。这句话出自Bruce Eckel,原文是:Life is short,you need Python。使用过Python语言的程序员,或从其他的语言(比如Java,php等)转换到Python开发的Coder, 对这句话的理解更加深刻。开发方便,高效明了,语言健全等等优点是这句话的最好诠释。伴随着大数据和人工智能的高速发展,python对这方面的优势尤其明显。既然人生苦短,那么高效准确稳定的执行就显得格外重要,既然如此,那多进程操作一定要研究下。下面结合案例,一点点让你看到多进程的庐山真面目。




python中multi函数用法 multiple python_python 多进程

Python多进程



Multiprocessing模块

python多进程主要是通过multiprocessing模块来实现的,而在使用时用到各种方法,下面就常用类的进行详细介绍。




python中multi函数用法 multiple python_python中multi函数用法_02

Multiprocessing



Process类




python中multi函数用法 multiple python_python中multi函数用法_03

Multiprocessing Process



构造方法

Process([group [, target [, name [, args [, kwargs]]]]])其中:

  • group: 线程组,目前还没有实现,库引用中提示必须是None;
  • target: 要执行的方法;
  • name: 进程名;
  • args/kwargs: 要传入方法的参数。

实例方法:

is_alive():返回进程是否在运行。

join([timeout]):阻塞当前上下文环境的进程程,直到调用此方法的进程终止或到达指定的3. timeout(可选参数)。

start():进程准备就绪,等待CPU调度。

run():strat()调用run方法,如果实例进程时未制定传入target,这star执行t默认run()方法。

terminate():不管任务是否完成,立即停止工作进程。

实例代码

# -*- coding:utf-8 -*-from multiprocessing import Processimport osimport time  def run_proc(name):    time.sleep(10)    print('Run child process %s (%s)...' % (name, os.getpid()))  def hello_world():    # time.sleep(5)    time.sleep(20)    print('hello world!')    print('Run child process (%s)...' % (os.getpid()))  if __name__ == '__main__':    print ('Parent process %s.' % os.getpid())    p1 = Process(target=run_proc, args=('test',))    p2 = Process(target=hello_world)    print 'Process will start.'    p1.start()    p2.start()    p1.join()    print('Process end.')

Pool类

如果目标少且不用控制进程数量则可以用Process方法,但如果需要指定数量的进程供用户调用,那么就要用Pool方法了。根据字面意思就可以理解了进程池,Pool类用于需要执行的目标很多,而手动限制进程数量又太繁琐时情况。当有新的请求提交到pool中时,如果池还没有满,那么就会创建一个新的进程用来执行该请求;但如果池中的进程数已经达到规定最大值,那么该请求就会等待,直到池中有进程结束,才会创建新的进程来执行它。在共享资源时,只能使用Multiprocessing.Manager类,而不能使用Queue或者Array。




python中multi函数用法 multiple python_java调用python获取返回值_04

Multiprocessing Pool



构造方法

Pool([processes[, initializer[, initargs[, maxtasksperchild[, context]]]]]),其中:

  • processes :使用的工作进程的数量,如果processes是None那么使用 os.cpu_count()返回的数量。
  • initializer: 如果initializer是None,那么每一个工作进程在开始的时候会调用initializer(*initargs)。
  • maxtasksperchild:工作进程退出之前可以完成的任务数,完成后用一个新的工作进程来替代原进程,来让闲置的资源被释放。maxtasksperchild默认是None,意味着只要Pool存在工作进程就会一直存活。
  • context: 用在制定工作进程启动时的上下文,一般使用 multiprocessing.Pool() 或者一个context对象的Pool()方法来创建一个池,两种方法都适当的设置了context。

实例方法

apply_async(func[, args[, kwds[, callback]]]) 它是非阻塞。

apply(func[, args[, kwds]])是阻塞的

close() 关闭pool,使其不在接受新的任务。

terminate() 关闭pool,结束工作进程,不在处理未完成的任务。

join() 主进程阻塞,等待子进程的退出, join方法要在close或terminate之后使用。

Pool使用方法

Pool+Map函数

此法的缺点:在于只能通过map向函数来传递一个参数。

from multiprocessing import Pooldef test(i):    print i    if __name__=="__main__":    lists=[1,2,3]    pool=Pool(processes=2) #定义最大的进程数    pool.map(test,lists)        #p必须是一个可迭代变量。    pool.close()    pool.join()

异步进程池(非阻塞)

from multiprocessing import Pooldef test(i):    print i    if __name__=="__main__":    pool = Pool(processes=10)    for i  in xrange(500):        '''        For循环中执行步骤:        (1)循环遍历,将500个子进程添加到进程池(相对父进程会阻塞)        (2)每次执行10个子进程,等一个子进程执行完后,立马启动新的子进程。(相对父进程不阻塞)        apply_async为异步进程池写法。        异步指的是启动子进程的过程,与父进程本身的执行(print)是异步的,而For循环中往进程池添加子进程的过程,与父进程本身的执行却是同步的。        '''        pool.apply_async(test, args=(i,)) #维持执行的进程总数为10,当一个进程执行完后启动一个新进程.           print “test”    pool.close()    pool.join()

执行说明:

For循环内执行了2个步骤,第一步:将500个对象放入进程池(阻塞)。第二步:同时执行10个子进程(非阻塞),有结束的就立即添加,维持10个子进程运行。(apply_async方法的会在执行完for循环的添加步骤后,直接执行后面的print语句,而apply方法会等所有进程池中的子进程运行完以后再执行后面的print语句)

注意:调用join之前,先调用close或者terminate方法,否则会出错。执行完close后不会有新的进程加入到pool,join函数等待所有子进程结束。

同步进程池(阻塞)

from multiprocessing import Pooldef test(p):       print p       time.sleep(3)if __name__=="__main__":    pool = Pool(processes=10)    for i  in xrange(500):    '''    实际测试发现,for循环内部执行步骤:    (1)遍历500个可迭代对象,往进程池放一个子进程    (2)执行这个子进程,等子进程执行完毕,再往进程池放一个子进程,再执行。(同时只执行一个子进程)    for循环执行完毕,再执行print函数。    '''        pool.apply(test, args=(i,))   #维持执行的进程总数为10,当一个进程执行完后启动一个新进程.    print “test”    pool.close()    pool.join()

说明:for循环内执行的步骤顺序,往进程池中添加一个子进程,执行子进程,等待执行完毕再添加一个子进程…..等500个子进程都执行完了,再执行print “test”。(从结果来看,并没有多进程并发)

子进程返回值

在实际使用多进程的时候,可能需要获取到子进程运行的返回值。如果只是用来存储,则可以将返回值保存到一个数据结构中;如果需要判断此返回值,从而决定是否继续执行所有子进程,则会相对比较复杂。另外在Multiprocessing中,可以利用Process与Pool创建子进程,这两种用法在获取子进程返回值上的写法上也不相同。=下面我们直接上代码,分析多进程中获取子进程返回值的不同用法,以及优缺点。

初级用法(Pool)

目的:存储子进程返回值

说明:如果只是单纯的存储子进程返回值,则可以使用Pool的apply_async异步进程池;当然也可以使用Process,用法与threading中的相同,这里只介绍前者。

实例:当进程池中所有子进程执行完毕后,输出每个子进程的返回值。

from multiprocessing import Pooldef test(p):         return pif __name__=="__main__":    pool = Pool(processes=10)    result=[]    for i  in xrange(50000):       '''       for循环执行流程:       (1)添加子进程到pool,并将这个对象(子进程)添加到result这个列表中。(此时子进程并没有运行)       (2)执行子进程(同时执行10个)       '''       result.append(pool.apply_async(test, args=(i,)))#维持执行的进程总数为10,当一个进程执行完后添加新进程.           pool.join()    '''    遍历result列表,取出子进程对象,访问get()方法,获取返回值。(此时所有子进程已执行完毕)    '''    for i in result:        print i.get()

错误写法:

for i  in xrange(50000):   t=pool.apply_async(test, args=(i,)))   print t.get()

说明:这样会造成阻塞,因为get()方法只能等子进程运行完毕后才能调用成功,否则会一直阻塞等待。如果写在for循环内容,相当于变成了同步,执行效率将会非常低。

高级用法(Pool)

目的:父进程实时获取子进程返回值,以此为标记结束所有进程。

实例一

执行子进程的过程中,不断获取返回值并校验,如果返回值为True则结果所有进程。

from multiprocessing import Poolimport Queueimport timedef test(p):    time.sleep(0.001)    if p==10000:        return True    else:        return Falseif __name__=="__main__":    pool = Pool(processes=10)    q=Queue.Queue()    for i  in xrange(50000):        '''        将子进程对象存入队列中。        '''        q.put(pool.apply_async(test, args=(i,)))#维持执行的进程总数为10,当一个进程执行完后添加新进程.           '''    因为这里使用的为pool.apply_async异步方法,因此子进程执行的过程中,父进程会执行while,获取返回值并校验。    '''    while 1:        if q.get().get():            pool.terminate() #结束进程池中的所有子进程。            break    pool.join()

说明:总共要执行50000个子进程(并发数量为10),当其中一个子进程返回True时,结束进程池。因为使用了apply_async为异步进程,因此在执行完for循环的添加子进程操作后(只是添加并没有执行完所有的子进程),可以直接执行while代码,实时判断子进程返回值是否有True,有的话结束所有进程。

优点:不必等到所有子进程结束再结束程序,只要得到想要的结果就可以提前结束,节省资源。

不足:当需要执行的子进程非常大时,不适用,因为for循环在添加子进程时,要花费很长的时间,虽然是异步,但是也需要等待for循环添加子进程操作结束才能执行while代码,因此会比较慢。

实例二

使用多线程+多进程的方式,添加了在执行子进程的过程中,不断获取返回值并校验,如果返回值为True则结果所有进程。

from multiprocessing import Poolimport Queueimport threadingimport timedef test(p):    time.sleep(0.001)    if p==10000:        return True    else:        return Falseif __name__=="__main__":    result=Queue.Queue() #队列    pool = Pool()    def pool_th():        for i  in xrange(50000000): ##这里需要创建执行的子进程非常多            try:                result.put(pool.apply_async(test, args=(i,)))            except:                break    def result_th():        while 1:            a=result.get().get() #获取子进程返回值            if a:                pool.terminate() #结束所有子进程                break    '''    利用多线程,同时运行Pool函数创建执行子进程,以及运行获取子进程返回值函数。    '''    t1=threading.Thread(target=pool_th)    t2=threading.Thread(target=result_th)    t1.start()    t2.start()    t1.join()    t2.join()    pool.join()

执行流程:利用多线程,创建一个执行pool_th函数线程,一个执行result_th函数线程,pool_th函数用来添加进程池,开启进程执行功能函数并将子进程对象存入队列,而result_th()函数用来不停地从队列中取子进程对象,调用get()方法获取返回值。等发现其中存在子进程的返回值为True时,结束所有进程,最后结束线程。

优点:弥补了实例(一)的不足,即使for循环的子进程数量很多,也能提高性能,因为for循环与判断子进程返回值同时进行。