python 多进程
python 多进程
一、创建多进程的基本法方法
创建进程的类:Process([group [, target [, name [, args [, kwargs]]]]]),target表示调用对象,args表示调用对象的位置参数元组。kwargs表示调用对象的字典。name为别名。group实质上不使用。
1、使用现成的Process类创建多线程
import multiprocessing import time def worker(interval): n = 5 while n > 0: print("The time is {0}".format(time.ctime())) time.sleep(interval) n -= 1 if __name__ == "__main__": p = multiprocessing.Process(target = worker, args = (3,)) p.start() print "p.pid:", p.pid print "p.name:", p.name print "p.is_alive:", p.is_alive()
输出结果: p.pid: 8736 p.name: Process-1 p.is_alive: True The time is Tue Apr 21 20:55:12 2015 The time is Tue Apr 21 20:55:15 2015 The time is Tue Apr 21 20:55:18 2015 The time is Tue Apr 21 20:55:21 2015 The time is Tue Apr 21 20:55:24 2015
二、使用自定义的类创建多线程
import multiprocessing import time class MyProcess(multiprocessing.Process): def __init__(self, interval): multiprocessing.Process.__init__(self) self.interval = interval def run(self): n = 5 while n > 0: print("the time is {0}".format(time.ctime())) time.sleep(self.interval) n -= 1 if __name__ == '__main__': p = MyProcess(3) p.start()
输出结果: the time is Wed Jul 20 22:44:16 2016 the time is Wed Jul 20 22:44:19 2016 the time is Wed Jul 20 22:44:22 2016 the time is Wed Jul 20 22:44:25 2016 the time is Wed Jul 20 22:44:28 2016
说明:自定义多线程类需要重写run方法,执行自定义类的实例时会自动调用run方法。
3、守护进程
1)守护进程即守护主进程的进程,与主进程同生共死。
2)主进程退出时,守护进程也跟着退出。
3)默认情况下,创建的紫禁城程非守护进程,既主进程如果执行完毕的,而子进程没有执行完毕,主进程会等待子进程执行完毕后再退出。
4)设置守护进程的方法是 setDaemon = True。
def worker(interval): print("work start:{0}".format(time.ctime())); time.sleep(interval) print("work end:{0}".format(time.ctime())); if __name__ == "__main__": p = multiprocessing.Process(target = worker, args = (3,)) p.daemon = True p.start() print("end!")
输出结果: end!
输出结果只有一个end!,子进程随着主进程的消亡而消亡,因为设置了守护进程。
4、进程间共享数据
默认情况下进程间不能共享数据
#-*-coding:utf8-*- import multiprocessing li = [] def foo(i): global li li.append(i) print('say hi', li) if __name__ == '__main__': for i in range(10): p = multiprocessing.Process(target=foo, args=(i,)) p.start() print('ending', li)
输出结果: say hi [1] say hi [0] say hi [2] say hi [3] say hi [4] say hi [5] ending [] say hi [6] say hi [8] say hi [7] say hi [9]
说明进程间没有共享数据
进程间共享内存空间的方法
方法一:使用multiprocess 自带的queues
from multiprocessing import Process from multiprocessing import queues import multiprocessing def foo(i,arg): arg.put(i) print('say hi',i,arg.qsize()) if __name__ == "__main__": # li = [] li = queues.Queue(20,ctx=multiprocessing) for i in range(10): p = Process(target=foo,args=(i,li,)) #p.daemon = True p.start() #p.join()
输出结果: say hi 7 2 say hi 6 3 say hi 2 3 say hi 5 4 say hi 1 6 say hi 3 6 say hi 4 8 say hi 0 8 say hi 9 9 say hi 8 10
说明:从输出结果中可以看到 队列的大小是在不断变化的,从而说明各个进程进程建共享了队列的数据。
方法二:使用multiprocessing的数据结构 Array
from multiprocessing import Process from multiprocessing import Array def foo(i,arg): arg[i] = i + 100 for item in arg: print(item) print('================') if __name__ == "__main__": li = Array('i', 3) for i in range(3): p = Process(target=foo,args=(i,li,)) p.start()
输出结果: 0 0 102 ================ 0 101 102 ================ 100 101 102 ================
说明:默认情况下 Array 初始化状态下,是一组内存地址连续的,数据类型相同的,初始值是0的一组数据。我们看到在多个进程的的作用下,Array 不同位置的元素,被连续修改。说明多个进程建共享了内存。
方法三:使用multiprocess的内置字典
from multiprocessing import Process from multiprocessing import Manager import multiprocessing def foo(i,arg): arg[i] = i + 100 print(arg.values()) if __name__ == "__main__": obj = Manager() li = obj.dict() for i in range(10): p = Process(target=foo,args=(i,li,)) p.start() p.join()
输出结果: [100] [100, 101] [100, 101, 102] [100, 101, 102, 103] [100, 101, 102, 103, 104] [100, 101, 102, 103, 104, 105] [100, 101, 102, 103, 104, 105, 106] [100, 101, 102, 103, 104, 105, 106, 107] [100, 101, 102, 103, 104, 105, 106, 107, 108] [100, 101, 102, 103, 104, 105, 106, 107, 108, 109]
说明:从输出结果上看,字典的值是不断增加的,说明多个进程间共享了字典的内存空间。
进程池
进程池会把进程的最大数量控制在一个值,进程池可以提供指定数量的进程供用户调用,当有新的请求提交到进程池中时,如果池还没有满,那么就会创建一个新的进程用来执行该请求;但如果进程池中的进程数已经达到规定最大值,那么该请求就会等待,直到池中有进程结束,才会创建新的进程来它。
1、使用阻塞的进程池
import multiprocessing import time def func(msg): print("msg:", msg) time.sleep(2) print("end") if __name__ == "__main__": pool = multiprocessing.Pool(processes=3) for i in range(5): msg = "mark %d" % i pool.apply(func, (msg, )) #维持执行的进程总数为processes,当一个进程执行完毕后会添加新的进程进去 print("proessed here ##################") pool.close() pool.join() #调用join之前,先调用close函数,否则会出错。执行完close后不会有新的进程加入到pool,join函数使主进程阻塞,等待所有子进程结束 print("done.")
输出结果: msg: mark 0 end msg: mark 1 end msg: mark 2 end msg: mark 3 end msg: mark 4 end proessed here ################## done.
2、使用异步的进程池(非阻塞)
import multiprocessing import time def func(msg): print("msg:", msg) time.sleep(2) print("end") if __name__ == "__main__": pool = multiprocessing.Pool(processes=3) for i in range(5): msg = "mark %d" % i pool.apply_async(func, (msg, )) #维持执行的进程总数为processes,当一个进程执行完毕后会添加新的进程进去 print("proessed here ##################") pool.close() pool.join() #调用join之前,先调用close函数,否则会出错。执行完close后不会有新的进程加入到pool,join函数使主进程阻塞,等待所有子进程结束 print("done.")
输出结果: proessed here ################## msg: mark 0 msg: mark 1 msg: mark 2 end msg: mark 3 end msg: mark 4 end end end done.
相关函数说明:
1、apply_async(func[, args[, kwds[, callback]]])
与apply用法一致,但它是非阻塞的且支持结果返回后进行回调。
主进程循环运行过程中不等待apply_async的返回结果,在主进程结束后,即使子进程还未返回整个程序也会退出。虽然 apply_async是非阻塞的,但其返回结果的get方法却是阻塞的,如使用result.get()会阻塞主进程。
如果我们对返回结果不感兴趣, 那么可以在主进程中使用pool.close与pool.join来防止主进程退出。注意join方法一定要在close或terminate之后调用。
2、close()
关闭pool,使其不再接受新的任务。
3、terminate()
结束工作进程,不再处理未处理的任务。
4、join()
主进程阻塞等待子进程的退出, join方法要在close或terminate之后使用。
3、获取进程池中进程的返回结果
import multiprocessing import time def func(msg): print("msg:", msg) time.sleep(3) print("end") return "done" + msg if __name__ == "__main__": pool = multiprocessing.Pool(processes=4) for i in range(5): msg = "hello %d" % i print(pool.apply_async(func, (msg,)).get()) pool.close() pool.join()
输出结果: msg: hello 0 end donehello 0 msg: hello 1 end donehello 1 msg: hello 2 end donehello 2 msg: hello 3 end donehello 3 msg: hello 4 end donehello 4
说明:我们看到,当获取进程池中进程的结果时,输出结果是阻塞的。正如上边apply_sync函数的解释,当要获取进程的结果时,执行的过程就会变成阻塞的。