由于Python设计的限制(我说的是咱们常用的CPython)。最多只能用满1个CPU核心。

Python提供了非常好用的多进程包multiprocessing,你只需要定义一个函数,Python会替你完成其他所有事情。借助这个包,可以轻松完成从单进程到并发执行的转换。

1、新建单一进程

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

import multiprocessing
import time
 
def 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,就变成阻塞版本了。

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

import multiprocessing
import time
 
def 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 multiprocessing
import time

def func(msg):
    for i in xrange(3):
	    print msg
	    time.sleep(1)
    return "done " + msg

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."

根据网友评论中的反馈,在Windows下运行有可能崩溃(开启了一大堆新窗口、进程),可以通过如下调用来解决:

multiprocessing         .         freeze_support         (         )

附加知识:

python自2.6开始提供了多进程模块multiprocessing,这里主要是介绍multiprocessing下的Pool的几个函数

一 apply(func[, args[, kwds]])
   apply用于传递不定参数,同python中的apply函数一致(不过内置的apply函数从2.3以后就不建议使用了),主进程会阻塞于函数。

for x in gen_list(l):
     result = pool.apply(pool_test, (x,))
     print 'main process'


这个时候主进程的执行流程同单进程一致
二 apply_async(func[, args[, kwds[, callback]]])
   与apply用法一致,但它是非阻塞的且支持结果返回后进行回调。

for x in gen_list(l):
     result = pool.apply_async(pool_test, (x,))
     print 'main process'

   这个时候主进程循环运行过程中不等待apply_async的返回结果,在主进程结束后,即使子进程还未返回整个程序也会就退出。虽然 apply_async是非阻塞的,但其返回结果的get方法却是阻塞的,在本例中result.get()会阻塞主进程。因此可以这样来处理返回结果:
    [x.get() for x in [pool.apply_async(pool_test, (x,)) for x in gen_list(l)]]
如果我们对返回结果不感兴趣, 那么可以在主进程中使用pool.close与pool.join来防止主进程退出。注意join方法一定要在close或terminate之后调用。

for x in gen_list(l):
     pool.apply_async(pool_test, (x, ))
     print 'main_process'
     pool.close()
     pool.join()

三 map(func, iterable[, chunksize])
   map方法与内置的map函数行为基本一致,在它会使进程阻塞与此直到结果返回。
   但需注意的是其第二个参数虽然描述的为iterable, 但在实际使用中发现只有在整个队列全部就绪后,程序才会运行子进程。
四 map_async(func, iterable[, chunksize[, callback]])
   与map用法一致,但是它是非阻塞的。其有关事项见apply_async。
五 imap(func, iterable[, chunksize])
   与map不同的是, imap的返回结果为iter,需要在主进程中主动使用next来驱动子进程的调用。即使子进程没有返回结果,主进程对于gen_list(l)的 iter还是会继续进行, 另外根据python2.6文档的描述,对于大数据量的iterable而言,将chunksize设置大一些比默认的1要好。
   for x in pool.imap(pool_test, gen_list(l)):
       pass
六 imap_unordered(func, iterable[, chunksize])
   同imap一致,只不过其并不保证返回结果与迭代传入的顺序一致。
七 close()
   关闭pool,使其不在接受新的任务。
八 terminate()
   结束工作进程,不在处理未处理的任务。
九 join()

l = range(10)
 def gen_list(l):
     for x in l:
         print 'yield', x
         def pool_test(x):    
    print 'f2', x     
    time.sleep(1)