Python asyncio - 使用asyncio.Event()阻止消费者

时间:2016-12-10 19:52:24

标签: python asynchronous python-3.5 python-asyncio

我有一个程序包含一个生产者和两个消费者,我想用协同程序重写它,以便每个消费者只处理最后一个值(即跳过在处理旧值时生成的新值)为它生成的(我使用了线程和threading.Queue()但是在put()上使用了块,导致队列在大多数时间都是满的。)< / p>

阅读answer to this question后,我决定使用asyncio.Eventasyncio.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行上的q.get()协程阻止,并且没有打印任何内容。

在两者中添加asyncio.sleep()之后,我的预期效果正常(我得 1,11,21,... ):

import asyncio
import time

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

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()
        await asyncio.sleep(0.1)
        # 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()

......但是我没有它就在寻找解决方案。

为什么会这样?我该如何解决?我想我不能从await l()拨打m,因为他们都有状态(在原始程序中,第一个使用PyGame绘制解决方案,第二个绘图结果)。

1 个答案:

答案 0 :(得分:1)

代码无法正常工作,因为运行m函数的任务永远不会停止。在event.is_set()== False的情况下,任务将继续递增i。由于此任务永远不会挂起,因此永远不会调用任务运行函数l。因此,您需要一种方法来暂停任务运行功能m。暂停的一种方法是等待另一个协程,这就是为什么asyncio.sleep按预期工作的原因。

我认为以下代码可以按预期工作。 LeakyQueue将确保消费者只处理生产者的最后一个值。由于复杂性非常对称,消费者将消耗生产者产生的所有价值。如果增加delay参数,则可以模拟消费者仅处理生产者创建的最后一个值。

batch.end()