1.进程间的通信方式
1.磁盘交互: 速度慢,不安全
2.socket套接字
3.管道通信(Pipe)
4.消息队列(Queue, Manager().Queue, JoinableQueue)
5.共享内存(Value, Array)
6.信号(os.kill, signal)
7.信号量(Semaphore)
8.共享数据(Manager)
2.管道通信-Pipe
1.概述: 在内存中开辟一块空间,对多个进程可见,通过管道实现多进程通信
2.语法
from multiprocess import Pipe fd1, fd2 = Pipe(duplex=True) # duplex默认为True表示双向管道,设置False则表示单向管道 返回值: 返回两个管道流对象,表示管道的两端 如果 duplex=True 则是双向管道则 fd1可读可写,fd2可读可写 如果 duplex=False 则是单向管道则 fd1只能,读fd2只能写 date = fd1.recv() # 接收并返回接收的消息(每次接收一条),如果管道没有消息会阻塞等待 fd2.send(data) # 发送消息,可以是字符串或其他类型,date为要发送的内容,如果没有接收端则管道破裂
3.示例1
from multiprocessing import Process from multiprocessing import Pipe import time import os # 创建一个双向管道 fd1, fd2 = Pipe() # 如果参数为False,则表示创建一个单向管道,此时fd1只等recv,fd2只能send # fd1, fd2 = Pipe(False) def fun(name): time.sleep(1) # 每个子进程都向管道中发送字符串消息 fd1.send("hello %s" % name) # 收发支持Python的多种数据类型,数值,字符串,列表等等 print("fun进程的pid是:%s 父进程的pid是:%s" % (os.getpid(), os.getppid())) jobs = list() for i in range(5): p = Process(target=fun, args=(i,)) jobs.append(p) p.start() # 父进程从管道中取消息 for i in range(5): data = fd2.recv() print(data) for i in jobs: i.join() """执行结果 fun进程的pid是:24334 父进程的pid是:24333 hello 0 hello 1 fun进程的pid是:24335 父进程的pid是:24333 hello 2 fun进程的pid是:24336 父进程的pid是:24333 hello 3 fun进程的pid是:24337 父进程的pid是:24333 hello 4 fun进程的pid是:24338 父进程的pid是:24333 """
4.示例2
from multiprocessing import Pipe from multiprocessing import Process def func(con): con1, con2 = con con1.close() # 子进程使用con2和父进程通信,所以关闭con1 while 1: try: print(con2.recv()) # 当主进程的con1发数据时,子进程要死循环的去接收 except EOFError: # 如果主进程的con1发完数据并关闭con1,子进程的con2继续接收时,就会报错,使用try的方式,获取错误 con2.close() # 获取到错误,就是指子进程已经把管道中所有数据都接收完了,所以用这种方式去关闭管道 break if __name__ == '__main__': con1, con2 = Pipe() p = Process(target=func, args=((con1, con2),)) p.start() con2.close() # 在父进程中,使用con1去和子进程通信,所以不需要con2,就提前关闭 for i in range(10): # 生产数据 con1.send(i) # 给子进程的con2发送数据 con1.close() # 生产完数据,关闭父进程这一端的管道
3.消息队列通信-Queue, Manager().Queue, JoinableQueue
1.消息队列概述
在内存中开辟队列模型,用来存放消息,任何拥有队列的进程都可以存取消息,消息队列是先进先出
from queue import Queue # 是进程内非阻塞队列即线程队列,类似于普通列表
from multiprocessing import Queue # 是跨进程通信队列,用于解决多进程间的通信问题
from multiprocessing import Manager # 是进程池中各子进程间的通信,使用锁 lock = manager.Queue().Lock()
2.语法
q = Queue(maxsize=0) # 创建一个消息队列,返回消息队列对象 参数: maxsize默认为0表示队列可存放消息,不指定或数量为负值时容量由内存而定,大于0表示队列最多存放多少条消息 q.put(item,[block[, timeout]]) # 将item消息写入队列,当队列满时会阻塞,要存放的消息(字符串,整数,列表) 可选参数: block: 默认为True表示阻塞,这种为False则非阻塞 timeout: 在block为True时设置超时时间,单位是秒, 例如: q.put("test", True, 3) 如果block值为默认的True 没有设置timeout,消息列队如果已经没有空间可写入,此时程序将被阻塞(停在写入状态),直到从消息列队腾出空间为止 设置了timeout,则会等待timeout秒,若还没空间,则抛出"Queue.Full"异常 如果block值为False: 消息列队如果没有空间可写入,则会立刻抛出"Queue.Full"异常 q.get([block[, timeout]]) # 向队列中取出并返回消息,然后将其从列队中移除,当队列空时会阻塞 可选参数: block: 默认为True表示阻塞,设置为False则非阻塞 timeout: 在block为True时设置超时时间,单位是秒, 例如 q.get(True, 3) 如果block值为True 没有设置timeout,消息列队如果为空,此时程序将被阻塞(停在读取状态),直到从消息列队读到消息为止 设置了timeout,则会等待timeout秒,若还没读取到任何消息,则抛出"Queue.Empty"异常 如果block值为False: 消息列队如果为空,则会立刻抛出"Queue.Empty"异常 q.put_nowait(item) # 用法相当于 q.put(item, False) q.get_nowait() # 用法相当于 q.get(False) q.full() # 判断队列是否为满,满则返回True q.empty() # 判断队列是否为空,空则返回True q.qsize() # 得到当前队列中消息的个数 q.close() # 关闭队列
3.进程队列
from multiprocessing import Process from multiprocessing import Queue import time import random # 写数据进程执行的代码: def write(q): for value in ['A', 'B', 'C']: print('Put %s to queue...' % value) q.put(value) time.sleep(random.random()) # 读数据进程执行的代码: def read(q): while True: if not q.empty(): value = q.get(True) print('Get %s from queue.' % value) time.sleep(random.random()) else: break if __name__ == '__main__': # 父进程创建Queue,并传给各个子进程: q = Queue() pw = Process(target=write, args=(q,)) pr = Process(target=read, args=(q,)) # 启动子进程pw,写入: pw.start() # 等待pw结束: pw.join() # 启动子进程pr,读取: pr.start() pr.join() # pr进程里是死循环,无法等待其结束,只能强行终止: print("") print("所有数据都写入并且读完") """执行结果 Put A to queue... Put B to queue... Put C to queue... Get A from queue. Get B from queue. Get C from queue. """
4.进程池队列
# 进程池中使用队列,修改import中的Queue为Manager from multiprocessing import Manager from multiprocessing import Pool import os import time def reader(q): print("reader启动(%s),父进程为(%s)" % (os.getpid(), os.getppid())) for i in range(q.qsize()): print("reader从Queue获取到消息:%s" % q.get(True)) def writer(q): print("writer启动(%s),父进程为(%s)" % (os.getpid(), os.getppid())) for i in "itcast": q.put(i) if __name__ == "__main__": print("(%s) start" % os.getpid()) q = Manager().Queue() # 使用Manager中的Queue po = Pool() po.apply_async(writer, (q,)) time.sleep(1) # 先让上面的任务向Queue存入数据,然后再让下面的任务开始从中取数据 po.apply_async(reader, (q,)) po.close() po.join() print("(%s) End" % os.getpid()) """执行结果 (24474) start writer启动(24476),父进程为(24474) reader启动(24477),父进程为(24474) reader从Queue获取到消息:i reader从Queue获取到消息:t reader从Queue获取到消息:c reader从Queue获取到消息:a reader从Queue获取到消息:s reader从Queue获取到消息:t (24474) End """
5.进程队列实现生产者消费者模型
# 实现方案一: 生产者生产结束的标识,放到生产者进程中 from multiprocessing import Queue from multiprocessing import Process # 生产者 def consumer(q, name): while 1: info = q.get() if info: print('%s 拿走了%s' % (name, info)) else: # 当消费者获得队列中数据时,如果获得的是None,就是获得到了生产者不再生产数据的标识 break # 此时消费者结束即可 # 消费者 def producer(q, product): for i in range(20): info = product + '的智能手机%s号' % str(i) q.put(info) q.put(None) # 让生产者生产完数据后,给消费者一个不再生产数据的标识 if __name__ == '__main__': q = Queue(10) p_pro = Process(target=producer, args=(q, '中国制造')) p_con = Process(target=consumer, args=(q, 'Kali')) p_pro.start() p_con.start() """执行结果 Kali 拿走了中国制造的手办0号 Kali 拿走了中国制造的手办1号 Kali 拿走了中国制造的手办2号 Kali 拿走了中国制造的手办3号 Kali 拿走了中国制造的手办4号 Kali 拿走了中国制造的手办5号 Kali 拿走了中国制造的手办6号 Kali 拿走了中国制造的手办7号 Kali 拿走了中国制造的手办8号 Kali 拿走了中国制造的手办9号 Kali 拿走了中国制造的手办10号 Kali 拿走了中国制造的手办11号 Kali 拿走了中国制造的手办12号 Kali 拿走了中国制造的手办13号 Kali 拿走了中国制造的手办14号 Kali 拿走了中国制造的手办15号 Kali 拿走了中国制造的手办16号 Kali 拿走了中国制造的手办17号 Kali 拿走了中国制造的手办18号 Kali 拿走了中国制造的手办19号 """ # 实现方案二: 生产者生产结束的标识,放到父进程中 from multiprocessing import Queue from multiprocessing import Process # 生产者 def consumer(q, name, color): while 1: info = q.get() if info: print('%s%s 拿走了%s 33[0m' % (color, name, info)) else: # 当消费者获得队列中数据时,如果获得的是None,就是获得到了生产者不再生产数据的标识 break # 此时消费者结束即可 # 消费者 def producer(q, product): for i in range(10): info = product + '的智能手机%s号' % str(i) q.put(info) if __name__ == '__main__': q = Queue(10) # 多个生产者进程 p_pro1 = Process(target=producer, args=(q, '中国制造')) p_pro2 = Process(target=producer, args=(q, '美国制造')) p_pro3 = Process(target=producer, args=(q, '日本制造')) # 多个消费者进程 p_con1 = Process(target=consumer, args=(q, 'Kali', '