简体   繁体   English

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

[英]Python asyncio - consumer blocking with asyncio.Event()

I have a program with one producer and two slow consumers and I'd like to rewrite it with coroutines in such way that each consumer will handle only last value (ie skip new values generated during processing the old ones) produced for it (I used threads and threading.Queue() but with it blocks on put() , cause the queue will be full most of the time). 我有一个包含一个生产者和两个消费者的程序,我想用协程重写它,这样每个消费者将处理为其生成的最后一个值 (即跳过处理旧值时产生的新值)(我用过线程和threading.Queue()但在put()上阻塞了该对象,因为该队列大部分时间都已满)。

After reading answer to this question I decided to use asyncio.Event and asyncio.Queue . 阅读此问题的答案后,我决定使用asyncio.Eventasyncio.Queue I wrote this prototype program: 我写了这个原型程序:

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()

and I have noticed that l coroutine blocks on q.get() line and doesn't print anything. 而且我注意到l协程在q.get()行上阻塞,并且不打印任何内容。

It works as I expect after adding asyncio.sleep() in both (I get 1,11,21,... ): 在两者中都添加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()

...but I'm looking for solution without it. ...但是我正在寻找没有它的解决方案。

Why is it so? 为什么会这样呢? How can I fix it? 我该如何解决? I think I cannot call await l() from m as both of them have states (in original program the first draws solution with PyGame and the second plots results). 我想我不能从m调用await l() ,因为它们都具有状态(在原始程序中,第一个使用PyGame绘制解决方案,第二个绘制结果)。

The code is not working as expected as the task running the m function is never stopped. 代码未按预期运行,因为运行m函数的任务永远不会停止。 The task will continue increment i in the case that event.is_set() == False. 在event.is_set()== False的情况下,任务将继续递增i。 Because this task is never suspended, the task running function l will never be called. 由于此任务永远不会挂起,因此永远不会调用任务运行功能l。 Therefore, you need a way to suspend the task running function m. 因此,您需要一种挂起任务运行功能m的方法。 One way of suspending is awaiting another coroutine, that is the reason why a asyncio.sleep works as expected. 挂起的一种方法是等待另一个协程,这就是asyncio.sleep按预期工作的原因。

I think the following code will work as you expect. 我认为以下代码将按您期望的那样工作。 The LeakyQueue will ensure that only the last value from the producer will be processed by the consumer. LeakyQueue将确保消费者仅处理生产者的最后一个值。 As the complexity is very symmetric, the consumer will consume all values produced by the producer. 由于复杂性非常对称,因此消费者将消耗生产者产生的所有价值。 If you increase the delay argument, you can simulate that the consumer only processes the last value created by the producer. 如果增加delay参数,则可以模拟消费者仅处理生产者创建的最后一个值。

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))

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM