简体   繁体   中英

Are asyncio.Event wait'ers guaranteed to be notified of the Event being set

Is a coroutine waiting for an asyncio.Event to be set guaranteed to be notified every time the event is set?

Neither Event.set() nor Event.clear() are awaitables. Thus an Event can be set and cleared without control being returned to the loop. Thus I guess calling set() and clear() will not notify the waiters on the event. The only way I found to make sure the waiters are notified is by adding an asycnio.sleep(0) to force back the control to the loop inbetween the set() and the clear(). But this looks rather ugly to me.

Here is an example:

NUM_EVENTS = 1000
NUM_WAITERS = 1000

import asyncio

async def waiter(event, id):
    print('waiting for it ...')
    for i in range(NUM_EVENTS):
        await event.wait()
        await asyncio.sleep(0)  # might be dropped by affects order
        print(f'... {id} got {i}')

async def main():
    # Create an Event object.
    event = asyncio.Event()

    # Spawn a Task to wait until 'event' is set.
    waiter_tasks = []
    for i in range(NUM_WAITERS):
        waiter_tasks.append(asyncio.create_task(waiter(event, i)))

    # Sleep for 1 second and set the event.
    await asyncio.sleep(1)
    for i in range(NUM_EVENTS):
        event.set()
        await asyncio.sleep(0)  # leaving this out will result in the waiters only receiving the event once
        event.clear()

    # Wait until the waiter task is finished.
    await asyncio.wait(waiter_tasks)

if __name__ == '__main__':
    asyncio.run(main())

Leaving out the asyncio.sleep(0) on line 31 will make the waiters receiving the event only once (instead of 1000) times.

But how to do this cleanly?

Is a coroutine waiting for an asyncio.Event to be set guaranteed to be notified every time the event is set?

A waiter is only guaranteed to notice that a set() occurred while it was waiting, even if it was immediately followed by clear() . There is no guarantee that it will be woken up as many time as the state was changed from false to true.

This stronger guarantee would require an additional counter on the object, which would no longer be a simple boolean "event". It would also be incompatible with threading.Event , which doesn't provide it.

But how to do this cleanly?

The clean way would be for the Event to only mark that "something" has happened, and that the metainformation what has happened (which includes the counter, if needed) is stored alongside. That way the event object does a single purpose: signaling to the waiters that the event they've been waiting for has transpired, and they can loop the requisite number of times themselves.

If the waiters need to react to set() immediately , then awaiting asyncio.sleep(0) is the way to go about it, but in asyncio that's somewhat of a design smell. It's hard to tell how to fix it without knowing more about your code, though.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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