Python asyncio-使用asyncio.Event()的使用者阻塞

Python asyncio-使用asyncio.Event()的使用者阻塞,python,asynchronous,python-3.5,python-asyncio,Python,Asynchronous,Python 3.5,Python Asyncio,我有一个程序,有一个生产者和两个慢速消费者,我想用协程重写它,这样每个消费者只处理为它生成的最后一个值(即跳过在处理旧值过程中生成的新值)(我使用了线程和threading.Queue()但它会阻塞put(),导致队列大部分时间都已满) 阅读之后,我决定使用asyncio.Event和asyncio.Queue。我写了这个原型程序: import asyncio async def l(event, q): h = 1 while True: # ready

我有一个程序,有一个生产者和两个慢速消费者,我想用协程重写它,这样每个消费者只处理为它生成的最后一个值(即跳过在处理旧值过程中生成的新值)(我使用了线程和
threading.Queue()
但它会阻塞
put()
,导致队列大部分时间都已满)

阅读之后,我决定使用
asyncio.Event
asyncio.Queue
。我写了这个原型程序:

import asyncio

async def l(event, q):
    h = 1
    while True:
        # ready
        event.set()
        # get value to process
        a = await q.get()
        # process it
        print(a * h)
        h *= 2

async def m(event, q):
    i = 1
    while True:
        # pass element to consumer, when it's ready
        if event.is_set():
            await q.put(i)
            event.clear()
        # produce value
        i += 1

el = asyncio.get_event_loop()
ev = asyncio.Event()
qu = asyncio.Queue(2)
tasks = [
            asyncio.ensure_future(l(ev, qu)),
            asyncio.ensure_future(m(ev, qu))
        ]
el.run_until_complete(asyncio.gather(*tasks))
el.close()
我注意到,
l
coroutine阻塞了
q.get()
行,并且不打印任何内容

在两者中都添加了
asyncio.sleep()
(我得到1,11,21,)之后,它的工作方式与我预期的一样:

…但我正在寻找没有它的解决方案


为什么会这样?我怎样才能修好它?我想我不能从
m
调用
wait l()
,因为它们都有状态(在原始程序中,第一个用PyGame绘制解决方案,第二个绘制结果)。

由于运行m函数的任务从未停止,代码没有按预期工作。如果event.is_set()==False,任务将继续递增i。由于此任务从未挂起,因此将永远不会调用运行函数l的任务。因此,您需要一种暂停任务运行函数m的方法。挂起的一种方式是等待另一个协同路由,这就是asyncio.sleep按预期工作的原因

我认为下面的代码将如您所期望的那样工作。LeakyQueue将确保使用者只处理来自生产者的最后一个值。由于复杂性是非常对称的,消费者将消费生产者产生的所有价值。如果增加延迟参数,则可以模拟使用者仅处理生产者创建的最后一个值

import asyncio

class LeakyQueue(asyncio.Queue):
    async def put(self, item):
        if self.full():
            await self.get()
        await super().put(item)

async def consumer(queue, delay=0):
    h = 1
    while True:
        a = await queue.get()
        if delay:
            await asyncio.sleep(delay)
        print ('consumer', a)
        h += 2

async def producer(queue):
    i = 1
    while True:
        await asyncio.ensure_future(queue.put(i))
        print ('producer', i)
        i += 1

loop = asyncio.get_event_loop()
queue = LeakyQueue(maxsize=1)
tasks = [
    asyncio.ensure_future(consumer(queue, 0)),
    asyncio.ensure_future(producer(queue))
]
loop.run_until_complete(asyncio.gather(*tasks))

代码未按预期工作,因为运行m函数的任务从未停止。如果event.is_set()==False,任务将继续递增i。由于此任务从未挂起,因此将永远不会调用运行函数l的任务。因此,您需要一种暂停任务运行函数m的方法。挂起的一种方式是等待另一个协同路由,这就是asyncio.sleep按预期工作的原因

我认为下面的代码将如您所期望的那样工作。LeakyQueue将确保使用者只处理来自生产者的最后一个值。由于复杂性是非常对称的,消费者将消费生产者产生的所有价值。如果增加延迟参数,则可以模拟使用者仅处理生产者创建的最后一个值

import asyncio

class LeakyQueue(asyncio.Queue):
    async def put(self, item):
        if self.full():
            await self.get()
        await super().put(item)

async def consumer(queue, delay=0):
    h = 1
    while True:
        a = await queue.get()
        if delay:
            await asyncio.sleep(delay)
        print ('consumer', a)
        h += 2

async def producer(queue):
    i = 1
    while True:
        await asyncio.ensure_future(queue.put(i))
        print ('producer', i)
        i += 1

loop = asyncio.get_event_loop()
queue = LeakyQueue(maxsize=1)
tasks = [
    asyncio.ensure_future(consumer(queue, 0)),
    asyncio.ensure_future(producer(queue))
]
loop.run_until_complete(asyncio.gather(*tasks))

为什么不简单地依赖队列
maxsize
,而不是使用
事件
?(
queue.put
阻塞,直到队列中有可用的插槽)@Vincent是的,对,但正如我在开头所写,我不希望它阻塞
put
。我有一个关于线程和
线程的解决方案。Queue
,但有了它,生产者几乎一直在等待最慢的消费者。我想尝试使用协同路由和
asyncio.Event
行为,这让我感到困惑,因此问题就来了。为什么不简单地依赖队列
maxsize
,而不是使用
事件
?(
queue.put
阻塞,直到队列中有可用的插槽)@Vincent是的,对,但正如我在开头所写,我不希望它阻塞
put
。我有一个关于线程和
线程的解决方案。Queue
,但有了它,生产者几乎一直在等待最慢的消费者。我想尝试使用协同程序和
asyncio.Event
行为,这让我感到困惑,因此提出了这个问题。