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=[]就可以.  

 

 

协程间的通信比线程间的通信要简单的.

 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)    收藏  举报

导航