python中的多线程其实并不是真正的多线程,如果想要充分地使用多核CPU的资源,在python中大部分情况需要使用多进程。Python提供了非常好用的多进程包multiprocessing,只需要定义一个函数,Python会完成其他所有事情。借助这个包,可以轻松完成从单进程到并发执行的转换。multiprocessing支持子进程、通信和共享数据、执行不同形式的同步,提供了Process、Queue、Pipe、Lock等组件。
一个最简单的多进程例子。
#!/usr/bin/env python # -*- coding:utf-8 -*- from multiprocessing import Pool from time import sleep def func(x): sleep(1) return x*x if __name__ == '__main__': p = Pool(5) print p.map(func,[1,2,3,4,5]) 这个例子的内容是计算1,2,3,4,5的平方,但是调用了多进程,一共五个进程。每个进程分别取计算平方。
再看另一种多进程的实现方法。
#!/usr/bin/env python from multiprocessing import Process import os def info(title): print title print 'moudle name:',__name__ if hasattr(os,'getppid'): print 'parent process',os.getppid() print 'process id:',os.getpid() def func(name): info('function func') print 'hello',name if __name__ == '__main__': info('function main') print '------------------' p = Process(target=func,args=('Jack',)) p.start() p.join() 看看执行的结果。
function main moudle name: __main__ parent process 4298 process id: 6323 ------------------ function func moudle name: __main__ parent process 6323 process id: 6324 hello Jack
内容是,创建了一个进程去执行了一个function func 函数。可以清楚看到,父子进程之间的关系。
多进程之间的通信
首先进程之间默认是不能共享数据的,而线程是可以的。
#!/usr/bin/env python from multiprocessing import Process import threading def run(info_list,n): info_list.append(n) print info_list info = [] print '------- process ----------' for i in range(10): p = Process(target=run,args=[info,i]) p.start() p.join() print '------- threading --------' info = [] for i in range(10): t = threading.Thread(target=run,args=[info,i]) t.start() t.join()
结果是:
------- process ---------- [0] [1] [2] [3] [4] [5] [6] [7] [8] [9] ------- threading -------- [0] [0, 1] [0, 1, 2] [0, 1, 2, 3] [0, 1, 2, 3, 4] [0, 1, 2, 3, 4, 5] [0, 1, 2, 3, 4, 5, 6] [0, 1, 2, 3, 4, 5, 6, 7] [0, 1, 2, 3, 4, 5, 6, 7, 8] [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
由此可见,进程之间是不共享数据的。而线程是可以的。
将所有进程产生的数据都放进Queue里面。这样的话,x进程可以去使用y进程产生的数据。
#!/usr/bin/python from multiprocessing import Process,Queue def func(q,n): q.put(['index',n]) if __name__ == '__main__': q = Queue() for i in range(5): p = Process(target=func,args=(q,i)) p.start() while True: print q.get() 执行结果是。可以完全的取出0到4.
结果: ['index', 2] ['index', 4] ['index', 0] ['index', 1] ['index', 3]
第二,通过pipe进行多进程间通信。
pipe对象返回的元组分别代表的是管道的两端,管道默认的是全双工。两端都支持send和recv方法。两个进程分别操作管道两端的时候不会有冲突,两个进程对管道一端同时读写可能会有冲突:
如果定义了 p = Pipe(duplex=False)的单向管道,则p[0]只负责接受消息,p[1]只负责发送消息。
from multiprocessing import Pipe, Process def func(p): p.send([1, 'str', None]) p.close() if __name__ == "__main__": parent_side, child_side = Pipe() p = Process(target=func, args=(child_side,)) p.start() print parent_side.recv() p.join() 第三,防止访问冲突。
通过加锁的方式可以对一个资源实现独享。
from multiprocessing import Lock, Process def lock_func(l, number): l.acquire() print "Number is: %d" % number l.release() if __name__ == "__main__": l = Lock() for number in range(10): Process(target=lock_func, args=(l, number,)).start()
一开始的时候就使用线程池,操作十分简便。
from multiprocessing import Pool import time def f(x): print x*x time.sleep(1) return x*x pool = Pool(processes=4) res_list = [] for i in range(10): res = pool.apply_async(f,[i,]) res.get(timeout=2) 使用apply_async的非阻塞型。而apply是非阻塞型。get()方法可以取得返回值。不然的话返回的是一个进程池的对象。