python 3.x asyncio的通信Queue
asyncio的通信Queue
asyncio的通信Queue
- 协程里面的通信Queue需要使用asyncio提供的Queue,不要使用多线程的Queue.
- 因为多线程Queue内部使用的是condition,实际上会阻塞的.往多线程Queue的里面put数据的时候,如果队列已满是会阻塞的,取数据的时候,如果队列为空也是会阻塞的,所有多线程的Queue是不适用asyncio编程的.
- 使用方法与线程中的Queue是一样的.
分析asyncio的Queue的源码:
- asyncioQueue的接口与多线程的Queue的接口是一样的.
- 因为put()和get()都是协程,所以在调用put()和get()的时候,在put()和get()前添加await关键字
源码full():
1 def full(self): 2 if self._maxsize <= 0: 3 return False 4 else: 5 return self.qsize() >= self._maxsize
源码asyncio Queue的put(self, item)协程:
- put()协程完全的改写,也不会用到condition,因为是单线程,不会涉及到多线程的问题.所以不需要使用condition.
1 @coroutine 2 def put(self, item): 3 # 1.判断,往队列放数据的时候,如果消息队列已满,一定会去调用yield from putter这种模式,因为不能阻塞. 4 # 如果消息队列已满,self.full()为true 5 while self.full(): 6 putter = self._loop.create_future() 7 # 2.将future对象(putter)放到_putters双端队列里面. 8 self._putters.append(putter) 9 try: 10 # 3.这个地方就会暂停 11 # 3.1 谁来驱动yield from使代码继续下去呢??? 12 # 3.2 很明显消息队列有数据取出的情况下,就是消息队列不为空之后,才会驱动yield from 13 # 获取消息队列的数据就涉及到协程get() 14 yield from putter 15 except: 16 putter.cancel() 17 if not self.full() and not putter.cancelled(): 18 self._wakeup_next(self._putters) 19 raise 20 # 4.因为驱动yield from putter继续向下执行,就会跑到这个地方来.会执行put_nowait()方法. 21 # 4.如果消息队列没有满的话,会执行put_nowait()方法 22 return self.put_nowait(item)
源码put_nowait()函数:
1 def put_nowait(self, item): 2 if self.full(): 3 raise QueueFull 4 # 5._put()方法直接将数据放到_queue中,而_queue是一个双端队列 5 self._put(item) 6 self._unfinished_tasks += 1 7 self._finished.clear() 8 # 6._put数据进来之后,执行_wakeup_next()函数,协程put()就会驱动getters(就是协程get())去运行 9 self._wakeup_next(self._getters)
源码_put()函数:
1 def _put(self, item): 2 self._queue.append(item)
源码_init()函数:
1 def _init(self, maxsize): 2 self._queue = collections.deque()
源码_wakeup_next()方法:
1 def _wakeup_next(self, waiters): 2 while waiters: 3 waiter = waiters.popleft() 4 if not waiter.done(): 5 waiter.set_result(None) 6 break
源码asyncio Queue的get(self)协程:
1 @coroutine 2 def get(self): 3 while self.empty(): 4 getter = self._loop.create_future() 5 self._getters.append(getter) 6 try: 7 yield from getter 8 except: 9 getter.cancel() 10 11 try: 12 self._getters.remove(getter) 13 except ValueError: 14 pass 15 16 if not self.empty() and not getter.cancelled(): 17 self._wakeup_next(self._getters) 18 raise 19 # 3.3 get_nowait()方法 20 return self.get_nowait()
源码get_nowait()方法:
1 def get_nowait(self): 2 if self.empty(): 3 raise QueueEmpty 4 item = self._get() 5 # 3.4 调用_wakeup_next()方法,这个地方与前面看到的_wakeup_next()是一个方法,但是放进来的队列是_putters. 6 # _putters里面存放的是协程put()的future 7 self._wakeup_next(self._putters) 8 return item
源码_wakeup_next()方法:
1 def _wakeup_next(self, waiters): 2 while waiters: 3 # 3.5 在waiters中取出一个数据, 4 waiter = waiters.popleft() 5 if not waiter.done(): 6 # 3.6 然后对waiter执行set_result()方法,set_result()方法会驱动协程waiter去运行. 7 # 这个waiter就是协程put()里面的putter,所以set_result()方法会驱动协程put()中的yield from putter语句 8 # 继续向下执行代码 9 waiter.set_result(None) 10 break
疑问:
- asyncio中的Queue是一个单线程,实际上完全可以直接声明一个全局变量queue=[],实际上是能够达到消息通信的一个机制.
- 但为什么还要使用asyncio中的Queue呢???
- 是因为asyncio中的Queue有一个最大长度maxsize,当我们想去限流(限制流量,限制速度)的时候,使用asyncio中Queue就能达到限流的目的.
但是如果仅仅只是为了协程间的一个通信,不会在乎限流的作用的话,实际上直接使用一个全局变量queue=[]就可以.
- 是因为asyncio中的Queue有一个最大长度maxsize,当我们想去限流(限制流量,限制速度)的时候,使用asyncio中Queue就能达到限流的目的.
协程间的通信比线程间的通信要简单的.
1 import asyncio 2 from asyncio import Lock, Queue 3 4 queue = Queue() 5 6 7 async def test(): 8 # 注意 : 9 # 因为get()是一个协程,所以前面一定要加上await关键字. 10 await queue.get()
********
posted on 2019-05-27 07:49 jaydenjune 阅读(87) 评论(0) 收藏 举报
浙公网安备 33010602011771号