how to prevent blocking in python asyncio - python

Assume the following code:
import asyncio
import time
async def say_after():
for i in range(10):
await asyncio.sleep(1)
print("first function")
async def say_after2():
for i in range(10):
await asyncio.sleep(1)
print("second function")
async def main():
await say_after()
await say_after2()
asyncio.run(main())
It would first print "first function" 10 times and after function say_after is finished then it would print "second function" 10 times. now i want to run both functions at the same time like 2 threads (like parallel) with asycio not sequential executing. how do i do it?

Related

add task to running loop and run until complete

I have a function called from an async function without await, and my function needs to call async functions. I can do this with asyncio.get_running_loop().create_task(sleep()) but the run_until_complete at the top level doesn't run until the new task is complete.
How do I get the event loop to run until the new task is complete?
I can't make my function async because it's not called with await.
I can't change future or sleep. I'm only in control of in_control.
import asyncio
def in_control(sleep):
"""
How do I get this to run until complete?
"""
return asyncio.get_running_loop().create_task(sleep())
async def future():
async def sleep():
await asyncio.sleep(10)
print('ok')
in_control(sleep)
asyncio.get_event_loop().run_until_complete(future())
It appears that the package nest_asyncio will help you out here. I've also included in the example fetching the return value of the task.
import asyncio
import nest_asyncio
def in_control(sleep):
print("In control")
nest_asyncio.apply()
loop = asyncio.get_running_loop()
task = loop.create_task(sleep())
loop.run_until_complete(task)
print(task.result())
return
async def future():
async def sleep():
for timer in range(10):
print(timer)
await asyncio.sleep(1)
print("Sleep finished")
return "Sleep return"
in_control(sleep)
print("Out of control")
asyncio.get_event_loop().run_until_complete(future())
Result:
In control
0
1
2
3
4
5
6
7
8
9
Sleep finished
Sleep return
Out of control
[Finished in 10.2s]

Execute multiple async functions at the same time

I want to execute multiple async functions at the same time, here is an example
import asyncio
async def sayhello():
await asyncio.sleep(5)
print("hello")
async def saybye():
print("Bye")
async def execute():
await sayhello()
await saybye()
asyncio.run(execute())
Like, i want to execute "sayhello" and "saybye" at the same time, so first is gonna say bye, wait 5 seconds, and say hello, Not wait 5 seconds and say "hello, bye"

Python 3.7, async/await prints in wrong order

I was testing Python3.7 async/await functions and written simple code like this:
import asyncio
import time
async def first():
print('FIRST')
async def second():
time.sleep(2)
print('SECOND')
async def main():
asyncio.create_task(second())
asyncio.create_task(second())
asyncio.create_task(second())
asyncio.create_task(first())
asyncio.create_task(first())
asyncio.create_task(first())
asyncio.run(main())
When I run this code, it prints this:
SECOND
SECOND
SECOND
FIRST
FIRST
FIRST
Which is not what I expected. I thought FIRSTs should be printed first because they don't have delay. However, create_task waits until second() is finished before proceeding to text async task.
Is it possible to execute async tasks without making them to wait each other?
You have to use the gather() function and modify a bit your code. You also have to use the await keyword.
import asyncio
import time
async def first():
print('FIRST')
await asyncio.sleep(1)
print('SECOND')
async def main():
await asyncio.gather(first(), first(), first())
asyncio.run(main())
You can't use the time.sleep() function since it is not asynchronous, resulting in an actual sleep for the entire program. use the asyncio.sleep() function and await it so it runs asynchronously:
import asyncio
async def first():
print('FIRST')
async def second():
# The Fix:
await asyncio.sleep(2)
print('SECOND')
async def main():
await asyncio.gather(
second(),
second(),
second(),
first(),
first(),
first()
)
if __name__ == '__main__':
asyncio.run(main())
The result should be:
FIRST
FIRST
FIRST
SECOND
SECOND
SECOND

Why running an asynchronous coroutine does not yield control back?

I have a sample code:
import asyncio
import time
async def asyncsleep(number):
time.sleep(number)
async def do_one():
await asyncsleep(1)
print("one 1")
await asyncsleep(1)
print("one 2")
await asyncsleep(1)
print("one 3")
async def do_two():
await asyncsleep(1)
print("two 1")
await asyncsleep(1)
print("two 2")
await asyncsleep(1)
print("two 3")
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait([
do_one(),
do_two()
]))
loop.close()
This code is expected to have the two functions run parallel. I mean the output of both should come at the same time. But it is not happening. The reason is that I have implemented an sleep function myself asyncsleep. If I use asyncio.sleep instead of it, everything works fine.
import asyncio
import time
async def asyncsleep(number):
time.sleep(number)
async def do_one():
await asyncio.sleep(1)
print("one 1")
await asyncio.sleep(1)
print("one 2")
await asyncio.sleep(1)
print("one 3")
async def do_two():
await asyncio.sleep(1)
print("two 1")
await asyncio.sleep(1)
print("two 2")
await asyncio.sleep(1)
print("two 3")
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait([
do_one(),
do_two()
]))
loop.close()
I also have looked up the signature of asyncio.sleep function:
#coroutine
def sleep(delay, result=None, *, loop=None):
"""Coroutine that completes after a given time (in seconds)."""
if delay == 0:
yield
return result
if loop is None:
loop = events.get_event_loop()
future = loop.create_future()
h = future._loop.call_later(delay,
futures._set_result_unless_cancelled,
future, result)
try:
return (yield from future)
finally:
h.cancel()
What the asyncio.sleep function has that my implemented asyncsleep function does not have? They are both coroutines, Why are they behaving differently?
Async is not threads. Async code does not give up control until it hits an await or the function completes. Your sleep function doesn't give up control so:
The asyncio loop is running and do_one and do_two are scheduled. The loop chooses to run do_one and because you don't yield or await your code doesn't return to the loop until the function completes.
When you do asynchronous things, you do not have continiuous flow. There is an event loop, that has a pool of tasks and switches between them: when the current task awaits, the loop passes the control to one of the other tasks in the pool.
time.sleep, as gracefully stated in comments by #user2357112, is synchronous, it just blocks the execution for some time, not giving the opportunity to proceed to a different coroutine.
So, what asyncio.sleep has and your asyncsleep has not:
It creates a new Future (a special object representing the result of a task that is not yet completed)
That new task does nothing, but takes specified time
So the new future will yield the result when the time comes
That task helps to block a flow in which the asyncio.sleep was awaited but does not block the other tasks.
Feel the difference.

How to start coroutines and continue with synchronous tasks?

I am trying to understand asyncio and port my undestanding of threading. I will take the example of two threads running indefinitely and a non-threaded loop (all of them outputting to the console).
The threading version is
import threading
import time
def a():
while True:
time.sleep(1)
print('a')
def b():
while True:
time.sleep(2)
print('b')
threading.Thread(target=a).start()
threading.Thread(target=b).start()
while True:
time.sleep(3)
print('c')
I now tried to port this to asyncio based on the documentation.
Problem 1: I do not understand how to add the non-threaded task as all examples I saw show an ongoing loop at the end of the program which governs the asyncio threads.
I then wanted to have at least the two first threads (a and b) running in parallel (and, worst case, add the third c as a thread as well, abandonning the idea of mixed thread and non-threded operations):
import asyncio
import time
async def a():
while True:
await asyncio.sleep(1)
print('a')
async def b():
while True:
await asyncio.sleep(2)
print('b')
async def mainloop():
await a()
await b()
loop = asyncio.get_event_loop()
loop.run_until_complete(mainloop())
loop.close()
Problem 2: The output is a sequence of a, suggering that the b() coroutine is not called at all. Isn't await supposed to start a() and come back to the execution (and then start b())?
await stops execution at a point, you do await a(), and you have an infinite loop in a(), so it's logical b() doesn't get called. Think about it as if you insert a() in mainloop().
Consider this example:
async def main():
while True:
await asyncio.sleep(1)
print('in')
print('out (never gets printed)')
To achieve what you want you need to create a future which would manage multiple coroutines. asyncio.gather is for that.
import asyncio
async def a():
while True:
await asyncio.sleep(1)
print('a')
async def b():
while True:
await asyncio.sleep(2)
print('b')
async def main():
await asyncio.gather(a(), b())
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
loop.close()

Categories