how to accomplish concurrency when using asyncio.create_task - python-asyncio

It can accomplish concurrency with asyncio in the form below :
async def say_after(delay, what):
print(f'{what} {delay}')
await asyncio.sleep(delay)
print(f'over {what} {delay}')
async def main():
task1 = asyncio.create_task(say_after(1, "hello"))
task2 = asyncio.create_task(say_after(2, "world"))
print(f"started at {time.strftime('%X')}")
await task1
await task2
print(f"finished at {time.strftime('%X')}")
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
started at 10:18:54
hello 1
world 2
over hello 1
over world 2
finished at 10:18:56
But when I change the form of function 'main' into :
async def main():
print(f"started at {time.strftime('%X')}")
task1 = asyncio.create_task(say_after(1, "hello"))
await task1
task2 = asyncio.create_task(say_after(2, "world"))
await task2
print(f"finished at {time.strftime('%X')}")
It perfoms kind of sequential execution.
started at 10:18:33
hello 1
over hello 1
world 2
over world 2
finished at 10:18:36
So why dose the latter not perform in concurrency?

Related

Decorator to async function python

A simple decorate to calculate time a function takes to run:
import time
def decor(f):
starttime=time.time()
f()
print("child functoin run time is ", (time.time()-starttime)*1000, "ms")
return f
try to use it to decorate async functions:
async def sleep_and_print(seconds):
print(f"starting async {seconds} sleep 😴")
await asyncio.sleep(seconds)
print(f"finished async {seconds} sleep ⏰")
return seconds
#decor
async def main():
# using arguments
results = await asyncio.gather(sleep_and_print(3), sleep_and_print(6))
print(results)
asyncio.run(main())
I got RuntimeWarning: coroutine 'main' was never awaited error
If I change the decorator function to async and await, eg
async def decor(f):
starttime=time.time()
await f()
print("child functoin run time is ", (time.time()-starttime)*1000, "ms")
return f
asyncio.run(main()) failed with coroutine' object is not callable
Why main() becomes uncallable? Any suggestion on the work around?
Try to return async function from the decorator function:
import time
import asyncio
def decor(f):
async def _fn():
starttime = time.time()
await f()
print(
"child function run time is ",
(time.time() - starttime) * 1000,
"ms",
)
return _fn
async def sleep_and_print(seconds):
print(f"starting async {seconds} sleep 😴")
await asyncio.sleep(seconds)
print(f"finished async {seconds} sleep ⏰")
return seconds
#decor
async def main():
# using arguments
results = await asyncio.gather(sleep_and_print(3), sleep_and_print(6))
print(results)
asyncio.run(main())
Prints:
starting async 3 sleep 😴
starting async 6 sleep 😴
finished async 3 sleep ⏰
finished async 6 sleep ⏰
[3, 6]
child function run time is 6002.1820068359375 ms

Create an async generator "from scratch"

Most of the time I am using asyncio API. But how would I create an async generator "from scratch"?
Say I have a classic generator, and I want to make it async. How can I do that?
I naively thought that I could do something like below, but it does not run asynchronously (the 3 "for loops" are running one after the other, instead of concurrently). My hope was to make some_loop() asynchronous by calling it from some_async_loop() and releasing the event loop after each iteration with asyncio.sleep(0):
#!/usr/bin/env python3
import asyncio
async def run():
task = asyncio.ensure_future(run_async_loop())
asyncio.ensure_future(run_async_loop())
asyncio.ensure_future(run_async_loop())
await task
async def run_async_loop():
async for i in some_async_loop():
print(i)
async def some_async_loop():
for i in some_loop():
yield i
asyncio.sleep(0)
def some_loop():
for i in range(10):
yield i
if __name__ == "__main__":
loop = asyncio.get_event_loop()
loop.run_until_complete(run())
I'm glad the fixing the call to asyncio.sleep() got things running.
I'm concerned partly about your run() function, because you're only await-ing on the first task, which means your code could exit before the other tasks are complete. I would suggest this:
async def run():
tasks = [run_async_loop(), run_async_loop(), run_async_loop()]
await asyncio.gather(*tasks)
I think you can also simplify your __main__ block:
if __name__ == '__main__':
asyncio.run(run())

ValueError when running simple asyncio script

I'm learning about asycio and trying to run this script I get this error:
ValueError: a coroutine was expected, got <async_generator object
mygen at 0x7fa2af959a60>
What am I missing here?
import asyncio
async def mygen(u=10):
"""Yield powers of 2."""
i = 0
while i < int(u):
yield 2 ** i
i += 1
await asyncio.sleep(0.1)
asyncio.run(mygen(5))
The asyncio.run function expects a coroutine but mygen is an asynchronous generator.
You can try something like this:
test.py:
import asyncio
async def mygen(u=10):
"""Yield powers of 2."""
i = 0
while i < int(u):
yield 2**i
i += 1
await asyncio.sleep(0.1)
async def main():
async for i in mygen(5):
print(i)
if __name__ == "__main__":
asyncio.run(main())
Test:
$ python test.py
1
2
4
8
16
References:
What does the "yield" keyword do?
PEP 525 -- Asynchronous Generators

Python Multiprocessing not working (Discord.py + Windows 10)

The following code does not initiate, the pool or process method. Please help.
if __name__ == '__main__':
#client.event
async def on_message(message):
if message.guild is not None:
if (message.content.startswith('%season15')):
input = str(message.content[len('%season15'):].strip())
df = get_data(input) ##this line of code is processed
p = Pool(processes=3)
result = p.starmap(get_season_data, [(input,df,5),(input,df, 10),(input,df,15)])
processes = [Process(target=get_season_data, args=(symbol, df, i)) for i in [5,10,15]]
# Run processes
for p in processes:
p.start()
# Exit the completed processes
for p in processes:
p.join()
client.run('token#')
The code does initiate multiple python processes in task manager but does not process any of the code in get_season_data and does not throw any errors.

Different behaviour of asyncio.Task.all_tasks()

First example:
import asyncio
async def req():
print('request')
await asyncio.sleep(1)
async def run():
print(len(asyncio.Task.all_tasks()))
asyncio.ensure_future(req())
print(len(asyncio.Task.all_tasks()))
await asyncio.sleep(2)
print(len(asyncio.Task.all_tasks()))
loop = asyncio.get_event_loop()
loop.run_until_complete(run())
The result is:
1
2
request
1
Second example:
import asyncio
async def req():
print('request')
await asyncio.sleep(1)
async def run():
print(len(asyncio.Task.all_tasks()))
t = asyncio.ensure_future(req())
print(len(asyncio.Task.all_tasks()))
await t
print(len(asyncio.Task.all_tasks()))
loop = asyncio.get_event_loop()
loop.run_until_complete(run())
The result is:
1
2
request
2
So, why in first example the last call asyncio.Task.all_tasks() return 1 and in second example it's return 2?
In other words, why in first example the task, that wrap req() was deleted from a set of all tasks for an event loop, and why it is not true for the second example.
The task is removed from all_tasks() when it is destroyed.
Add a del statement:
[...]
await t
del t
print(len(asyncio.Task.all_tasks()))
and it will yield:
1
2
request
1

Resources