Python学习笔记17(多进程)
python中的多线程其实并不是真正的多线程,如果想要充分地使用多核CPU的资源,在python中大部分情况需要使用多进程。Python提供了非常好用的多进程包multiprocessing,只需要定义一个函数,Python会完成其他所有事情。借助这个包,可以轻松完成从单进程到并发执行的转换。multiprocessing支持子进程、通信和共享数据、执行不同形式的同步,提供了Process、Queue、Pipe、Lock等组件
一、使用多进程
进程也有两种调用方式、直接调用和继承式调用。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import multiprocessing,time def hello(i): print("start...",i) time.sleep(3) print("end...",i) if __name__ == "__main__": for i in range(3): p = multiprocessing.Process(target=hello,args=(i,)) p.start()
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import multiprocessing import time class My_Process(multiprocessing.Process): def __init__(self, i): multiprocessing.Process.__init__(self) self.i = i def run(self): print("start...",self.i) time.sleep(3) print("end...",self.i) if __name__ == '__main__': for i in range(3): p = My_Process(i) p.start()
与线程的使用方法类似,只是将threading.Thread换成了进程的multiprocessing.Process
p.deamon ------> 线程的setDeamon方法
p.join ------> 线程的join方法
二、Lock、RLock
声明方式:lock = multiprocessing.Lock(),其余同多线程
三、Semaphore
声明方式:s = multiprocessing.Semaphore(),其余同多线程
四、event
声明方式:e = multiprocessing.Event(),其余同多线程
五、Queue
Queue是多进程安全的队列,可以使用Queue实现多进程之间的数据传递。put方法用以插入数据到队列中,put方法还有两个可选参数:blocked和timeout。如果blocked为True(默认值),并且timeout为正值,该方法会阻塞timeout指定的时间,直到该队列有剩余的空间。如果超时,会抛出Queue.Full异常。如果blocked为False,但该Queue已满,会立即抛出Queue.Full异常。
get方法可以从队列读取并且删除一个元素。同样,get方法有两个可选参数:blocked和timeout。如果blocked为True(默认值),并且timeout为正值,那么在等待时间内没有取到任何元素,会抛出Queue.Empty异常。如果blocked为False,有两种情况存在,如果Queue有一个值可用,则立即返回该值,否则,如果队列为空,则立即抛出Queue.Empty异常。Queue的一段示例代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import multiprocessing import time def f(q): q.put([42, None, 'hello']) if __name__ == '__main__': q = multiprocessing.Queue() p = multiprocessing.Process(target=f, args=(q,)) p.start() print(q.get()) # prints "[42, None, 'hello']" p.join()
六、Pipe
Pipe方法返回(conn1, conn2)代表一个管道的两个端。Pipe方法有duplex参数,如果duplex参数为True(默认值),那么这个管道是全双工模式,也就是说conn1和conn2均可收发。duplex为False,conn1只负责接受消息,conn2只负责发送消息。
send和recv方法分别是发送和接受消息的方法。例如,在全双工模式下,可以调用conn1.send发送消息,conn1.recv接收消息。如果没有消息可接收,recv方法会一直阻塞。如果管道已经被关闭,那么recv方法会抛出EOFError。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import multiprocessing import time def proc1(pipe): while True: for i in range(10000): print("send: %s" %(i)) pipe.send(i) time.sleep(0.5) def proc2(pipe): while True: print("proc2 rev:", pipe.recv()) time.sleep(1) def proc3(pipe): while True: print("proc3 rev:", pipe.recv()) time.sleep(1) if __name__ == "__main__": pipe = multiprocessing.Pipe() p1 = multiprocessing.Process(target=proc1, args=(pipe[0],)) p2 = multiprocessing.Process(target=proc2, args=(pipe[1],)) p3 = multiprocessing.Process(target=proc3, args=(pipe[1],)) p1.start() p2.start() p3.start() p1.join() p2.join() p3.join()
七、Pool
Pool可以提供指定数量的进程,供用户调用,当有新的请求提交到pool中时,如果池还没有满,那么就会创建一个新的进程用来执行该请求;但如果池中的进程数已经达到规定最大值,那么该请求就会等待,直到池中有进程结束,才会创建新的进程来它。
- apply_async它是非阻塞,apply是阻塞的(主进程会被阻塞直到函数执行结束,不建议使用)
- close() 关闭pool,使其不在接受新的任务。
- terminate() 结束工作进程,不在处理未完成的任务。
- join() 主进程阻塞,等待子进程的退出, join方法要在close或terminate之后使用。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import multiprocessing import time def func(msg): print("msg:", msg) time.sleep(3) print("end") if __name__ == "__main__": pool = multiprocessing.Pool(processes = 3) for i in range(5): 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.") >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> #输出结果: Mark~ Mark~ Mark~~~~~~~~~~~~~~~~~~~~~~ msg: hello 0 msg: hello 1 msg: hello 2 end msg: hello 3 end msg: hello 4 end end end Sub-process(es) done.
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import multiprocessing import time def func(msg): print("msg:", msg) time.sleep(3) print("end") if __name__ == "__main__": pool = multiprocessing.Pool(processes = 3) for i in range(5): 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.") >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> #输出结果: msg: hello 0 end msg: hello 1 end msg: hello 2 end msg: hello 3 end msg: hello 4 end Mark~ Mark~ Mark~~~~~~~~~~~~~~~~~~~~~~ Sub-process(es) done.