Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Wait for timeout or event being set for asyncio.Event

I have a class with a method that looks like this:

# self.stop_event -> threading.Event
def run(self):
    while not self.stop_event.wait(3):  # i.e. repeat every 3 sec
        pass  # do stuff

The idea is that several of these are run in their own thread and at some point one thread does stop_event.set(), which naturally stops all others. I want to switch to asyncio for this, because the tasks in run are mostly sleeping and doing IO. Thus, I got to:

# self.stop_event -> asyncio.Event
async def run(self):
    while not self.stop_event.is_set():
        await asyncio.sleep(3)
        pass  # do stuff

The problem is that the asyncio.Event cannot be waited on, so when it is set, there are at most 3 seconds to wait before the method completes. This is a problem, because the sleep time may be minutes. Currently, I am working around this by wrapping the run in an asyncio.Task and then cancelling it like event_loop.call_soon(the_task.cancel).

I want to ask if there is a better way to achieve the above? Is there a way I can wait on an asyncio.Event with a timeout somehow, similar to the threading.Event?

like image 291
Ivan Kalchev Avatar asked Apr 03 '18 05:04

Ivan Kalchev


People also ask

What is Asyncio event ()?

An asyncio event can be used to notify multiple asyncio tasks that some event has happened. An Event object manages an internal flag that can be set to true with the set() method and reset to false with the clear() method. The wait() method blocks until the flag is set to true.

How do I stop Asyncio event loop?

Run an asyncio Event Loop run_until_complete(<some Future object>) – this function runs a given Future object, usually a coroutine defined by the async / await pattern, until it's complete. run_forever() – this function runs the loop forever. stop() – the stop function stops a running loop.

What is Asyncio wait?

asyncio. wait just waits on the futures. And instead of giving you the results directly, it gives done and pending tasks. You have to manually collect the values. Moreover, you could specify to wait for all futures to finish or just the first one with wait .

Does Asyncio need locks?

2) asyncio. Lock() is used to protect critical sections that call "yield from" - there is no need to use the lock otherwise. The discussion is under assumption of single-threaded asyncio usage mode.


1 Answers

Is there a way I can wait on an asyncio.Event with a timeout somehow, similar to the threading.Event?

asyncio.wait_for supports conveniently adding timeouts to any awaited coroutine. An emulation of the timeout feature of threading.Event.wait for asyncio.Event could look like this:

async def event_wait(evt, timeout):
    # suppress TimeoutError because we'll return False in case of timeout
    with contextlib.suppress(asyncio.TimeoutError):
        await asyncio.wait_for(evt.wait(), timeout)
    return evt.is_set()

This allows a run almost exactly like the one that used threading.Event:

async def run(self):
    while not await event_wait(self.stop_event, 3):
        pass  # do stuff
like image 186
user4815162342 Avatar answered Sep 19 '22 18:09

user4815162342