What purpose does asynchronous programming solve if using `await` pauses code continuation? - python

Using async await makes the methods execute on the UI thread by default. I have ran into situations where performing await on an expensive operation will result in the UI blocking (Like this post )
For example,
import asyncio
async def freeze_ui_5_seconds():
await asyncio.sleep(5)
awaiting this function on the UI thread will freeze the UI for 5 seconds. By utilizing await the code will pause and not continue until the await method has completed and a value can be returned. My question is, what does this achieve in the end if await essentially makes the code synchronous, that is, we will not continue until the await method has completed. Using await in my mind just retracts the idea of asynchronous programming as we are now "pausing" and "not continuing" until this method has completed and will freeze the UI.

Related

python async - efficiency of sleeping multiple coroutines

Context: I am making a discord bot, and the mute command comes with a duration specifier for when the user should be unmuted again (this is being done via assigning a role to the user that removes their ability to send messages).
Idea 1: I could make a loop that checks every, say, 30 seconds and looks through to see which mutes have expired and cancel them.
Idea 2: Each time a mute is processed, I could await asyncio.sleep(however long) and then cancel it.
I would like to ask - which is more idiomatic, and also more importantly, which is more efficient? The first one has the advantage that it only has one coroutine running whereas the last one spawns a new one for each individual case (I don't really know how many that might be, but let's say around 10 concurrent cases maximum). However, the last one is easier for me to implement and feels like a more direct solution, on top of making the unmute happen exactly on time instead of on a timed loop.
There is one idea to make a loop that awaits until the next task and then when it processes that, queue up the next one, but then you can't insert tasks at the front of the queue.
TL;DR - if I need to schedule multiple events, do I run a loop to constantly check the scheduler, or do I open a coroutine for each and just await asyncio.sleep(however long) each of them?
Your second idea seems the most accurate and straightforward as you can simply use asyncio.create_task to run an unmute coroutine after a certain amount of time. By using asyncio.create_task, you do not need to return or yield back the unmute coroutine to be gathered later or submit it to be checked by a loop at an established interval, for awaiting the returned response from asyncio.create_task will run the task in the background:
import asyncio
async def unmute(delay, user_id):
await asyncio.sleep(delay)
#run unmute process
async def mute(user_id):
#your mute function
mute_for = await do_mute(user_id) #some mute function
u = asyncio.create_task(unmute(mute_for, user_id))
await u #submitting the task to be run in the background

Python Asyncio confusion between asyncio.sleep() and time.sleep()

From the documentation, if we want to implement a non-blocking delay we should implement await asyncio.sleep(delay) because time.sleep(delay) is blocking in nature. But from what I could understand, it is the await keyword that cedes control of the thread to do something else while the function f() following the keyword i.e. await f() finishes its calculations.
So if we need the await keyword in order for asyncio.sleep(delay) to be non-blocking, what is the difference with its counterpart time.sleep(delay) if both are not awaited for?
Also, can't we reach the same result by preceding both sleep functions with a await keyword?
From one answer of somewhat similar topic:
The function asyncio.sleep simply registers a future to be called in x seconds while time.sleep suspends the execution for x seconds.
So the execution of that coroutine called await asyncio.sleep() is suspended until event loop of asyncio revokes after timer-expired event.
However, time.sleep() literally blocks execution of current thread until designated time is passed, preventing chance to run either an event loop or other tasks while waiting - which makes concurrency possible despite being single threaded.
For what I understand that's difference of following:
counting X seconds with stopwatch yourself
let the clock ticking and periodically check if X seconds has passed
You, a thread probably can't do other thing while looking at stopwatch yourself, while you're free to do other jobs between periodic check on latter case.
Also, you can't use synchronous functions with await.
From PEP 492 that implements await and async:
await, similarly to yield from, suspends execution of read_data coroutine until db.fetch awaitable completes and returns the result data.
You can't suspend normal subroutine, python is imperative language.
Also, can't we reach the same result by preceding both sleep functions with a await keyword?
No, that's the thing. time.sleep() blocks. You call it, then nothing will happen for a while, then your program will resume when the function returns. You can await at that point all you want, there's nothing to await.
asyncio.sleep() returns immediately, and it returns an awaitable. When you await that awaitable, the event loop will transfer control to another async function (if there is one), and crucially it will be notified when the awaitable is done, and resume the awaiting function at that point.
To illustrate what an awaitable is:
foo = asyncio.sleep()
print('Still active')
await foo
print('After sleep')
An awaitable is an object that can be awaited. You aren't awaiting the function call, you're awaiting its return value. There's no such return value with time.sleep.

How do I run an infinitely looping coroutine in a different thread?

I'm trying to run an infinite loop to check a web server every 20 seconds, and if it finds something, I want it to send a message to a discord channel via my discord bot. However, I'm neither quite sure how asyncio works because I haven't used async/await much nor do I know how to actually implement this.
I've tried a few things:
async def poll():
...
await send()
threading.Thread(target = poll).start()
This fails because I don't await the poll function. If I don't include the async in async def poll that obviously fails because then the await send() isn't valid.
async def poll():
...
await send()
asyncio.run_coroutine_threadsafe(poll(), asyncio.get_event_loop()) # this freezes the rest of my program and prevents my discord bot from working correctly
asyncio.run_coroutine_threadsafe(poll(), asyncio.new_event_loop()) # this warns me saying "coroutine poll was never awaited" and doesn't seem to execute the loop
I doubt I'm supposed to use threads with asyncio. But how would I make an infinite loop run in parallel to the rest of my code?
If you want this for a discord bot and using discord.py, then you can use discord.ext.tasks.loop or a background task.
Loop Example: Here
Background Task Example: Here
None of this will affect your bot, until you are not using a blocking module like requests or time.sleep()

Python asyncio wait_for synchronous

Using Python 3.6.8
async def sleeper():
time.sleep(2)
async def asyncio_sleeper():
await asyncio.sleep(2)
await asyncio.wait_for(sleeper(), 1)
await asyncio.wait_for(asyncio_sleeper(), 1)
Using time.sleep does NOT timeout, asyncio.sleep does timeout.
My intuition was that calling wait_for on a coroutine would base its timeout on how long the coroutine takes, not based on the individual async calls within the coroutine.
What is going on behind the scenes that results in this behavior, and is there a way to modify behavior to match my intuition?
What is going on behind the scenes that results in this behavior
The simplest answer is that asyncio is based on cooperative multitasking, and time.sleep doesn't cooperate. time.sleep(2) blocks the thread for two seconds, the event loop and all, and there is nothing anyone can do about it.
On the other hand, asyncio.sleep is carefully written so that when you await asyncio.sleep(2), it immediately suspends the current task and arranges with the event loop to resume it 2 seconds later. Asyncio's "sleeping" is implicit, which allows the event loop to proceed with other tasks while the coroutine is suspended. The same suspension system allows wait_for to cancel the task, which the event loop accomplishes by "resuming" it in such await that the await where it was suspended raises an exception.
In general, a coroutine not awaiting anything is good indication that it's incorrectly written and is a coroutine in name only. Awaits are the reason coroutines exist, and sleeper doesn't contain any.
is there a way to modify behavior to match my intuition?
If you must call legacy blocking code from asyncio, use run_in_executor. You will have to tell asyncio when you do so and allow it to execute the actual blocking call, like this:
async def sleeper():
loop = asyncio.get_event_loop()
await loop.run_in_executor(None, time.sleep, 2)
time.sleep (or other blocking function) will be handed off to a separate thread, and sleeper will get suspended, to be resumed when time.sleep is done. Unlike with asyncio.sleep(), the blocking time.sleep(2) will still get called and block its thread for 2 seconds, but that will not affect the event loop, which will go about its business similar to how it did when await asyncio.sleep() was used.
Note that cancelling a coroutine that awaits run_in_executor will only cancel the waiting for the blocking time.sleep(2) to complete in the other thread. The blocking call will continue executing until completion, which is to be expected since there is no general mechanism to interrupt it.

Python asyncio: how single thread can handle mutiple things simultaneously?

Hi I am new to asyncio and concept of event loops (Non-blocking IO)
async def subWorker():
...
async def firstWorker():
await subWorker()
async def secondWorker():
await asyncio.sleep(1)
loop = asyncio.get_event_loop()
asyncio.ensure_future(firstWorker())
asyncio.ensure_future(secondWorker())
loop.run_forever()
here, when code starts, firstWorker() is executed and paused until it encounters await subWorker(). While firstWorker() is waiting, secondWorker() gets started.
Question is, when firstWorker() encounters await subWorker() and gets paused, the computer then will execute subWorker() and secondWorker() at the same time. Since the program has only 1 thread now, and I guess the single thread does secondWorker() work. Then who executes subWorker()? If single thread can only do 1 thing at a time, who else does the other jobs?
The assumption that subWorker and secondWorker execute at the same time is false.
The fact that secondWorker simply sleeps means that the available time will be spent in subWorker.
asyncio by definition is single-threaded; see the documentation:
This module provides infrastructure for writing single-threaded concurrent code
The event loop executes a task at a time, switching for example when one task is blocked while waiting for I/O, or, as here, voluntarily sleeping.
This is a little old now, but I've found the visualization from the gevent docs (about 1 screen down, beneath "Synchronous & Asynchronous Execution") to be helpful while teaching asynchronous flow control to coworkers: http://sdiehl.github.io/gevent-tutorial/
The most important point here is that only one coroutine is running at any one time, even though many may be in process.

Categories