python的multiprocess的使用

来源:互联网 发布:网络测试仪怎么用 编辑:程序博客网 时间:2024/05/16 17:07

主要参考链接:

1)http://www.jb51.net/article/67116.htm

2)http://www.cnblogs.com/kaituorensheng/p/4445418.html

3)https://jingsam.github.io/2015/12/31/multiprocessing.html      写的很好

4)http://www.cnblogs.com/vamei/archive/2012/10/12/2721484.html



一。http://www.jb51.net/article/67116.htm的摘要

由于Python设计的限制(我说的是咱们常用的CPython)。最多只能用满1个CPU核心。这就是导致在做特征工程的时候,运行效率慢的主要原因,拿一个cpu单线程跑特征工程肯定慢。
Python提供了非常好用的多进程包multiprocessing,你只需要定义一个函数,Python会替你完成其他所有事情。借助这个包,可以轻松完成从单进程到并发执行的转换。


1、新建单一进程

如果我们新建少量进程,可以如下:

import multiprocessingimport timedef func(msg):  for i in xrange(3):    print msg    time.sleep(1)if __name__ == "__main__":  p = multiprocessing.Process(target=func, args=("hello", ))  p.start()  p.join()  print "Sub-process done."

2、使用进程池

是的,你没有看错,不是线程池。它可以让你跑满多核CPU,而且使用方法非常简单。

注意要用apply_async,如果落下async,就变成阻塞版本了,apply()这个表示上一个线程结束之后才能进行下一个任务,也就是前面所说的阻塞。如果是apply_async(),那么就可以异步执行。

processes=4是最多并发进程数量。

import multiprocessingimport timedef func(msg):  for i in xrange(3):    print msg    time.sleep(1)if __name__ == "__main__":  pool = multiprocessing.Pool(processes=4)  for i in xrange(10):    msg = "hello %d" %(i)    pool.apply_async(func, (msg, ))  pool.close()  pool.join()  print "Sub-process(es) done."

3、使用Pool,并需要关注结果

更多的时候,我们不仅需要多进程执行,还需要关注每个进程的执行结果,如下:

import multiprocessingimport timedef func(msg):  for i in xrange(3):    print msg    time.sleep(1)  return "done " + msg  #返回func的结果if __name__ == "__main__":  pool = multiprocessing.Pool(processes=4)  result = []  for i in xrange(10):    msg = "hello %d" %(i)    result.append(pool.apply_async(func, (msg, )))  pool.close()  pool.join()  for res in result:    print res.get()  print "Sub-process(es) done."

二。http://www.cnblogs.com/kaituorensheng/p/4445418.html

python中的多线程其实并不是真正的多线程,如果想要充分地使用多核CPU的资源,在python中大部分情况需要使用多进程。Python提供了非常好用的多进程包multiprocessing,只需要定义一个函数,Python会完成其他所有事情。借助这个包,可以轻松完成从单进程到并发执行的转换。multiprocessing支持子进程、通信和共享数据、执行不同形式的同步,提供了Process、Queue、Pipe、Lock等组件。


对于pool的讲解:

Pool

在利用Python进行系统管理的时候,特别是同时操作多个文件目录,或者远程控制多台主机,并行操作可以节约大量的时间。当被操作对象数目不大时,可以直接利用multiprocessing中的Process动态成生多个进程,十几个还好,但如果是上百个,上千个目标,手动的去限制进程数量却又太过繁琐,此时可以发挥进程池的功效。
Pool可以提供指定数量的进程,供用户调用,当有新的请求提交到pool中时,如果池还没有满,那么就会创建一个新的进程用来执行该请求;但如果池中的进程数已经达到规定最大值,那么该请求就会等待,直到池中有进程结束,才会创建新的进程来它。

 

例7.1:使用进程池(非阻塞)

复制代码
#coding: utf-8import multiprocessingimport timedef func(msg):    print "msg:", msg    time.sleep(3)    print "end"if __name__ == "__main__":    pool = multiprocessing.Pool(processes = 3)    for i in xrange(4):        msg = "hello %d" %(i)        pool.apply_async(func, (msg, ))   #维持执行的进程总数为processes,当一个进程执行完毕后会添加新的进程进去    print "Mark~ Mark~ Mark~~~~~~~~~~~~~~~~~~~~~~"    pool.close()    pool.join()   #调用join之前,先调用close函数,否则会出错。执行完close后不会有新的进程加入到pool,join函数等待所有子进程结束    print "Sub-process(es) done."
复制代码

一次执行结果

1
2
3
4
5
6
7
8
9
10
mMsg: hark~ Mark~ Mark~~~~~~~~~~~~~~~~~~~~~~ello 0
 
msg: hello 1
msg: hello 2
end
msg: hello 3
end
end
end
Sub-process(es) done.

函数解释:

  • apply_async(func[, args[, kwds[, callback]]]) 它是非阻塞,apply(func[, args[, kwds]])是阻塞的(理解区别,看例1例2结果区别)
  • close()    关闭pool,使其不在接受新的任务。
  • terminate()    结束工作进程,不在处理未完成的任务。
  • join()    主进程阻塞,等待子进程的退出, join方法要在close或terminate之后使用。

执行说明:创建一个进程池pool,并设定进程的数量为3,xrange(4)会相继产生四个对象[0, 1, 2, 4],四个对象被提交到pool中,因pool指定进程数为3,所以0、1、2会直接送到进程中执行,当其中一个执行完事后才空出一个进程处理对象3,所以会出现输出“msg: hello 3”出现在"end"后。因为为非阻塞,主函数会自己执行自个的,不搭理进程的执行,所以运行完for循环后直接输出“mMsg: hark~ Mark~ Mark~~~~~~~~~~~~~~~~~~~~~~”,主程序在pool.join()处等待各个进程的结束。

 

例7.2:使用进程池(阻塞)

复制代码
#coding: utf-8import multiprocessingimport timedef func(msg):    print "msg:", msg    time.sleep(3)    print "end"if __name__ == "__main__":    pool = multiprocessing.Pool(processes = 3)    for i in xrange(4):        msg = "hello %d" %(i)        pool.apply(func, (msg, ))   #维持执行的进程总数为processes,当一个进程执行完毕后会添加新的进程进去    print "Mark~ Mark~ Mark~~~~~~~~~~~~~~~~~~~~~~"    pool.close()    pool.join()   #调用join之前,先调用close函数,否则会出错。执行完close后不会有新的进程加入到pool,join函数等待所有子进程结束    print "Sub-process(es) done."
复制代码

一次执行的结果

1
2
3
4
5
6
7
8
9
10
msg: hello 0
end
msg: hello 1
end
msg: hello 2
end
msg: hello 3
end
Mark~ Mark~ Mark~~~~~~~~~~~~~~~~~~~~~~
Sub-process(es) done.

  

例7.3:使用进程池,并关注结果

复制代码
import multiprocessingimport timedef func(msg):    print "msg:", msg    time.sleep(3)    print "end"    return "done" + msgif __name__ == "__main__":    pool = multiprocessing.Pool(processes=4)    result = []    for i in xrange(3):        msg = "hello %d" %(i)        result.append(pool.apply_async(func, (msg, )))    pool.close()    pool.join()    for res in result:        print ":::", res.get()    print "Sub-process(es) done."
复制代码

一次执行结果

1
2
3
4
5
6
7
8
9
10
msg: hello 0
msg: hello 1
msg: hello 2
end
end
end
::: donehello 0
::: donehello 1
::: donehello 2
Sub-process(es) done.

 

例7.4:使用多个进程池

复制代码
#coding: utf-8import multiprocessingimport os, time, randomdef Lee():    print "\nRun task Lee-%s" %(os.getpid()) #os.getpid()获取当前的进程的ID    start = time.time()    time.sleep(random.random() * 10) #random.random()随机生成0-1之间的小数    end = time.time()    print 'Task Lee, runs %0.2f seconds.' %(end - start)def Marlon():    print "\nRun task Marlon-%s" %(os.getpid())    start = time.time()    time.sleep(random.random() * 40)    end=time.time()    print 'Task Marlon runs %0.2f seconds.' %(end - start)def Allen():    print "\nRun task Allen-%s" %(os.getpid())    start = time.time()    time.sleep(random.random() * 30)    end = time.time()    print 'Task Allen runs %0.2f seconds.' %(end - start)def Frank():    print "\nRun task Frank-%s" %(os.getpid())    start = time.time()    time.sleep(random.random() * 20)    end = time.time()    print 'Task Frank runs %0.2f seconds.' %(end - start)        if __name__=='__main__':    function_list=  [Lee, Marlon, Allen, Frank]     print "parent process %s" %(os.getpid())    pool=multiprocessing.Pool(4)    for func in function_list:        pool.apply_async(func)     #Pool执行函数,apply执行函数,当有一个进程执行完毕后,会添加一个新的进程到pool中    print 'Waiting for all subprocesses done...'    pool.close()    pool.join()    #调用join之前,一定要先调用close() 函数,否则会出错, close()执行后不会有新的进程加入到pool,join函数等待素有子进程结束    print 'All subprocesses done.'
复制代码

一次执行结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
parent process 7704
 
Waiting for all subprocesses done...
Run task Lee-6948
 
Run task Marlon-2896
 
Run task Allen-7304
 
Run task Frank-3052
Task Lee, runs 1.59 seconds.
Task Marlon runs 8.48 seconds.
Task Frank runs 15.68 seconds.
Task Allen runs 18.08 seconds.
All subprocesses done.

三。https://jingsam.github.io/2015/12/31/multiprocessing.html


pool.apply_async 采用异步方式调用 task,pool.apply 则是同步方式调用。同步方式意味着下一个 task 需要等待上一个 task 完成后才能开始运行,这显然不是我们想要的功能,所以采用异步方式连续地提交任务。在上面的语句中,我们提交了 4 个任务,假设我的 CPU 是 4 核,那么我的每个核运行一个任务。如果我提交多于 4 个任务,那么每个核就需要同时运行 2 个以上的任务,这回带来任务切换成本,降低了效率。所以我们设置的并行任务数最好等于 CPU 核心数, CPU 核可以通过下面语句得到:

1
cpus = multiprocessing.cpu_count()

接下来我们使用 result.get() 来获取 task 的返回值:

1
2
for result in results:
print(result.get())

在这里不免有人要疑问,为什么不直接在 for 循环中直接 result.get()呢?这是因为pool.apply_async之后的语句都是阻塞执行的,调用 result.get() 会等待上一个任务执行完之后才会分配下一个任务。事实上,获取返回值的过程最好放在进程池回收之后进行,避免阻塞后面的语句。

最后我们使用一下语句回收进程池:

1
2
pool.close()
pool.join()

最后附上完整的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def task(pid):
# do something
return result
def main():
multiprocessing.freeze_support()
pool = multiprocessing.Pool()
cpus = multiprocessing.cpu_count()
results = []
for i in xrange(0, cpus):
result = pool.apply_async(task, args=(i,))
results.append(result)
pool.close()
pool.join()
for result in results:
print(result.get())