队列(Queue)
什么是队列: 先进先出,与堆栈相反
队列方法
q.get(block, timeout) """ 这个是用来在队列中取值的,括号内不写参数,get一次取一次,如果q为空,就会阻塞在这里,block默认是true,如果设为false,取不到值则会返回一个异常,timeout后面跟超过的时间,如果超过这个时间,没用取到值,也会返回异常。 """ q.get_nowait() """ 这个呢也是,如果取不到就会返回一个异常 """ q = Queue() """ 实例化一个队列,括号内可以写队列容量,不写有一个默认值 """ q.qsize() """ 返回队列中目前值的正确数量。但是在某些系统上会引发异常 """ q.empty() """ 这个方法返回当前队列是否为空 """ q.full() """ 返回当前队列有没有满 """ put的用法和上面这些用法一样 例: from multiprocessing import Queue q = Queue(3) # q.put(1) # q.put(2) # q.put(3) # print(q.get()) # while True: # print(q.get_nowait()) # print(q.qsize()) # print(q.empty()) print(q.full())
验证进程间数据隔离
from multiprocessing import Process def f(): global n n = 100 print('子进程中的n:%s' % n) if __name__ == '__main__': p = Process(target=f) p.start() n = 0 print('主进程中的n: %s' % n)
解决进程间的数据隔离问题
from multiprocessing import Process, Queue import os def f(q): print(f'进程{os.getpid()}开始放数据') q.put(123) print(f'进程{os.getpid()}成功放了一条数据') if __name__ == '__main__': q = Queue(3) p = Process(target=f, args=(q, )) p.start() res = q.get() print(f'主进程{os.getpid()}取了一条数据{res}')
多进程放入数据多进程取
from multiprocessing import Process, Queue import os, time def task_put(q): info = str(os.getpid()) + '(put):' + str(time.asctime()) print(f'进程{os.getpid()}放了一条{info}') q.put(info) def task_get(q): info = q.get() print(f'进程{os.getpid()}取一条{info}') if __name__ == '__main__': q = Queue(3) get_list = [] put_list = [] for i in range(3): p = Process(target=task_get, args=(q, )) p.start() get_list.append(p) for j in range(3): p = Process(target=task_put, args=(q, )) p.start() put_list.append(p) for p in get_list: p.join() for p in put_list: p.join()
生产者消费者模型
生产者就是生产的线程,消费者就是消费的线程,而生产者消费者模式就是通过一个容器来解决生产者和消费者的强耦合。 # 版本一: # 单个生产者,单个消费者 def producer(q, name): for i in range(5): info = f'{name}{i}' q.put(info) print(f'{os.getpid()}生产了{info}') def customer(q): while True: info = q.get() print(f'{os.getpid()}消费了{info}') if __name__ == '__main__': q = Queue(3) p = Process(target=producer, args=(q, '包子')) p.start() c = Process(target=customer, args=(q, )) c.start() 上述版本的问题,在于,你在取的时候是一个死循环,取不到的时候,程序会阻塞在哪里 基于上述问题,解决办法一:给队列注入一个结束的信号,当取到这个信号时终止循环 # 版本二: # 单个生产者,单个消费者 def producer(q, name): for i in range(5): info = f'{name}{i}' q.put(info) print(f'{os.getpid()}生产了{info}') def customer(q): while True: info = q.get() if info == None: break print(f'{os.getpid()}消费了{info}') if __name__ == '__main__': q = Queue(3) p = Process(target=producer, args=(q, '包子')) p.start() c = Process(target=customer, args=(q, )) c.start() p.join() q.put(None)
这个结束的信号可以在子进程里面发,也可以在主进程里面发,不过在主进程里面发的时候,要等到生产者全部生产完才可以注入None结束信号
# 版本三: # 生产者>消费者 def producer(q, name): for i in range(5): info = f'{name}{i}' q.put(info) print(f'{os.getpid()}生产了{info}') def customer(q): while True: info = q.get() if info == None: break print(f'{os.getpid()}消费了{info}') if __name__ == '__main__': q = Queue(3) for i in range(3): p = Process(target=producer, args=(q, '包子' + str(i))) p.start() c = Process(target=customer, args=(q, )) c1 = Process(target=customer, args=(q, )) c.start() c1.start() p.join() q.put(None) q.put(None)
这个时生产者大于消费者的情况,在发结束信号的时候,几个消费者就发几个结束信号
# 版本三: # 生产者<消费者 def producer(q, name): for i in range(5): info = f'{name}{i}' q.put(info) print(f'{os.getpid()}生产了{info}') def customer(q, i): while True: try: info = q.get(timeout = 5) print(f'{i}消费了{info}') except Exception as e: print(e) break if __name__ == '__main__': q = Queue(3) for i in range(2): p = Process(target=producer, args=(q, '包子' + str(i))) p.start() for i in range(4): c = Process(target=customer, args=(q, i)) c.start()
这种情况,在发结束信号就有点不现实了,就可以用这种方法处理
from multiprocessing import Process, JoinableQueue def producer(q, name): for i in range(5): info = f'{name}{i}' q.put(info) print(f'{os.getpid()}生产了{info}') q.join() # 生产完毕,调用这个方法阻塞,直到队列中的所有项目被处理 def customer(q, i): while True: info = q.get(timeout = 5) print(f'{i}消费了{info}') q.task_done() # 像join发送一次信号,证明有一个数据被取走了 if __name__ == '__main__': q = JoinableQueue(3) for i in range(2): p = Process(target=producer, args=(q, '包子' + str(i))) p.start() for i in range(4): c = Process(target=customer, args=(q, i)) c.start()
这个方法不建议使用,当q.task_done()取不到值的时候,就会抛出异常,跟我们用timout时间,这个差不多一样