进程之间的通信--管道和队列
# 进程之间的通信----队列和管道 #进程间通信:IPC (Inter Process Communication) # 队列 #创建共享的进程队列,Queue是多进程安全的队列,可以使用Queue实现多进程之间的数据传递 # Queue([maxsize]) #创建共享的进程队列 #参数:maxsize是队列中允许的最大项数.如果省略这个参数,则无大小限制 #队列的底层使用管道和锁定实现(数据安全) #队列特点:先进先出(First In First Out) FIFO #栈的特点:先进后出(First In Last Out) FILO # Queue的实例具有以下几种方法: # q.get([block [,timeout]]) #返回q中的数据,如果q为空,此方法将阻塞,直到队列中有数据可以用为止 #block用于控制阻塞行为,默认为True,如果设置为,将引发Queue.Empty异常 #timeout是可选超出时间,用于阻塞模式中,如果在限定时间没有数据,将引发异常 # q.put(item [block[,timeout]]) # 将item放入队列,如果队列已满,次方法将阻塞停滞到有空间用为止. # block控制阻塞行为,默认为True ,如果设置为False,将引发Queue.Empty异常 #timeout指定在阻塞模式中等待可用空间的时间长短.超时候一发Queue.Full异常 # q.get_nowait() 同q.get(False)方法,没有数据获取就报错,不阻塞 # q.put_nowait() 同q.put(False)方法,没有空间放数据就报错,不阻塞 # q.close() 关闭队列,防止队列中加入更多数据.调用此方法时,后天线程将继续 #写入队列尚未写入的数据,但将此方法完成时马上关闭.如果q被垃圾收集,调用此方法.关闭 #队列不会再队列使用者中生成任何类型的数据结束信号或异常 # 单队列进程实例 #multiprocessing模块支持进程间通信的两种主要形式,管道和队列都是基于消息传递实现的, # from multiprocessing import Queue # # q = Queue(3) # 实例q 最大空间是3 # # q.put(2) # q.put(2) # q.put(3) # # q.put(3) # 如果队列已经满,程序阻塞等待 # # print(q.full()) # # q.put_nowait() # 如果队列已满,则报错 # try: # q.get_nowait(3) # 加入数据,满了抛出异常 # except: # print("队列已满") # # # # # print(q.get()) #获取数据 # print(q.get()) # print(q.get()) # # 结果: # # 2 # # 2 # # 3 # try: # print(q.get_nowait(33)) # 没有数据获取,抛出异常,程序继续执行 # except: # print("队列中没有数据") # # print(q.get()) # 继续获取数据,程序阻塞等待 # # print(q.get_nowait()) # 没有数据获取,抛出异常 # print(q.empty()) # True # # # # # 多进程之间的数据发送 #批量生产数据放入队列再批量获取结果 import os import time import multiprocessing # 向queue中输入数据的函数 def inputQ(queue): info = str(os.getpid()) + '(put):' + str(time.asctime()) queue.put(info) # 向queue中输出数据的函数 # def outputQ(queue): # info = queue.get() # print ('%s%s\033[32m%s\033[0m'%(str(os.getpid()), '(get):',info)) # # # Main # if __name__ == '__main__': # multiprocessing.freeze_support() # record1 = [] # store input processes # record2 = [] # store output processes # queue = multiprocessing.Queue(3) # # # 输入进程 # for i in range(10): # process = multiprocessing.Process(target=inputQ,args=(queue,)) # process.start() # record1.append(process) # # # 输出进程 # for i in range(10): # process = multiprocessing.Process(target=outputQ,args=(queue,)) # process.start() # record2.append(process) # # for p in record1: # p.join() # # for p in record2: # p.join() # # 批量生产数据放入队列再批量获取结果 x # 生产者消费者模型 #在并发编程中使用生产者和消费者模式能够解决绝大多数并发问题,该模式通过平衡生产线程 #和消费线程的工作能力来提高程序的整体处理数据的速度. # 为什么使用消费者生产者模式? #在线程世界里,生产者就是生产数据的线程,消费者就是消费数据的线程.在多线程 #开发当中,如果生产者速度很快,消费者处理速度很慢,那么生产者就必须等消费者处理完数据 #才能继续生产数据.同样,消费者速度太快,生产者跟不上,消费者也会等待,这样就浪费资源 #什么是消费者生产者模式: #生产者消费者模式是通过一个容器来解决生产者和消费者的强耦合问题.生产者和消费者 #彼此之间不直接通信,而是通过队列来进行通讯,所以生产者生产完数据之后不用等待消费者 #处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,队列就 #相当于一个缓冲区,平衡了生产者和消费者的处理能力 # 基于队列实现消费者生产者模型 # 版本一 # 结束信号None 由生产者发出 # from multiprocessing import Queue,Process # # def consumer(q,name): # 消费者 # while 1: # 消费者循环取数据 # info = q.get() # 消费者取数据 # if info: # print('%s拿到玩具了%s' %(name,info)) # else: # print("玩具领完") # # 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,'jack')) # 消费者 # p_pro.start() # p_con.start() # # from multiprocessing import Process,Queue # # def consumer(q,name): # while 1: # info = q.get() # 消费端获取数据 # if info: # print("%s拿到了%s号玩具" %(name,info)) # else: # print("玩具拿完了") # break # # def producer(q,product): # 生成者 # for i in range(20): # info = product + "生产了%s"%str(i) # q.put(info) # 生产端生成数据 # # # if __name__ == '__main__': # q = Queue(10) # p_pro = Process(target=producer,args=(q,"岛国")) # 生产者 # p_con = Process(target=consumer,args=(q,"alex")) # 消费者 # p_pro.start() # p_con.start() # # p_pro.join() # 等待 # q.put(None) # 在主进程端发送数据 # print("主进程") # # # 版本三 # 用 JoinableQueue 模块 # from multiprocessing import Process,JoinableQueue # # def consumer(q,name): # 消费者 # while 1: # info = q.get() # 消费数据 # if info: # print('%s拿到了%s' %(name,info)) # q.task_done() # 返回接收的数据 # # # def producer(q,product): # 生产者 # for i in range(20): # info = product + "的娃娃%s"%str(i) # q.put(info) # 生产数据 # q.join() # 接收task_done()返回的标识 # if __name__ == '__main__': # q = JoinableQueue(10) # p_pro = Process(target=producer,args=(q,"岛国")) # p_con = Process(target=consumer,args=(q,"alex")) # p_con.daemon = True # 将消费端设置守护进程,主进程代码结束,消费端结束,生产端也随之结束 # p_pro.start() # p_con.start() # p_pro.join() # 异步变同步,等待生产端结束,如果没有程序会堵塞
# 管道 #创建管道的类 # Pipe([duplex]):在进程之间创建一条管道,并返回元组(con1,con2) #con1和con2表示管道两端之间连接对象,强调一点,必须在产生Process对象之前产生管道 # 参数介绍 # dumplex:默认管道是全双工的,如果将duple设置成False,con1只能接收,con2只能发送 # 主要方法: # conn1.recv():接收conn2.send(obj) # 发送的对象。如果没有消息可接收,recv方法会一直阻塞。 # 如果连接的另外一端已经关闭,那么recv方法会抛出EOFError。 # conn1.send(obj):通过连接发送对象。obj是与序列化兼容的任意对象 # # # 其他方法: # conn1.close():关闭连接。如果conn1被垃圾回收,将自动调用此方法 # conn1.fileno():返回连接使用的整数文件描述符 # conn1.poll([timeout]):如果连接上的数据可用,返回True。timeout指定等待的最长时限。如果省略 # 此参数,方法将立即返回结果。如果将timeout射成None,操作将无限期地等待数据到达。 # # conn1.recv_bytes([maxlength]):接收c.send_bytes() # 方法发送的一条完整的字节消息。maxlength # 指定要接收的最大字节数。如果进入的消息,超过了这个最大值,将引发IOError异常,并且在连接上无法进 # 行进一步读取。如果连接的另外一端已经关闭,再也不存在任何数据,将引发EOFError异常。 # conn.send_bytes(buffer[, offset[, size]]):通过连接发送字节数据缓冲区,buffer是支持缓冲 # 区接口的任意对象,offset是缓冲区中的字节偏移量,而size是要发送字节数。结果数据以单条消息的形式发 # 出,然后调用c.recv_bytes() # 函数进行接收 # # conn1.recv_bytes_into(buffer[, offset]):接收一条完整的字节消息,并把它保存在buffer对象中, # 该对象支持可写入的缓冲区接口(即bytearray对象或类似的对象)。offset指定缓冲区中放置消息处的字节 # 位移。返回值是收到的字节数。如果消息长度大于可用的缓冲区空间,将引发BufferTooShort异常。 # Pipe的使用 from multiprocessing import Pipe, Process def func(con): con1, con2 = con con1.close() # 子进程使用con2和父进程通信,所以 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() # 生产完数据,关闭父进程这一端的管道 #