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
行为,这让我感到困惑,因此提出了这个问题。