Reputation: 21
I would like to know how can I order a program like this:
import asyncio
async def multi_coro():
async with asyncio.TaskGroup() as tg:
tg.create_task(coro('5'))
print('3-')
tg.create_task(coro('6'))
print('4-')
async def coro(i):
print(i)
await asyncio.sleep(.1)
print(i)
async def main():
async with asyncio.TaskGroup() as tg:
tg.create_task(multi_coro())
print('1-')
tg.create_task(coro('7'))
print('2-')
print('8-')
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
asyncio.run(main())
My goal is to get this return:
1-
2-
3-
4-
5
6
7
5
6
7
8-
But the return is:
1-
2-
3-
4-
7
5
6
7
5
6
8-
In use asyncio.gather(), the result is like TaskGroup.
async def gather():
print('3-')
await asyncio.gather(*[coro('4'), coro('5')])
print('6-')
So I would like to know how can I do to call tasks group in multi_coro before to call the second task in the main()
Edit:
import asyncio
async def another_coro(i):
print(i)
await asyncio.sleep(.1)
async def coro(i, tg):
if i == 1:
tg.create_task(another_coro(i * 10))
tg.create_task(another_coro(i * 100))
else:
print(i)
await asyncio.sleep(.1)
async def main():
async with asyncio.TaskGroup() as tg:
for i in range(0, 3):
tg.create_task(coro(i, tg))
asyncio.run(main())
printing is 0 => 2 => 10 => 100
But I would a method to get 0 => 10 => 100 => ... No matter what the sequence is, it is the result 10 and 100 directly after 0 that is sought.
Upvotes: 0
Views: 667
Reputation: 110476
The thing there is that your first task calling multi_coro
itself won't run until your code allows the asyncio loop to run, inside your main
code. Once it is awaited, as the TaskGroup it creates is exited before the co-routine finishes, the sub-coros it creates will also run.
Inserting a await asyncio.sleep(0)
creating the coro that will print 7
would work: but that would not be deterministic - the flow passes to the event loop, that will take the opportunity to step through existing tasks - but that behavior is implementation dependant.
exiting the task group and entering another, on the other hand, yes, will await all existing tasks created in that group, and will be a reliable behavior:
import asyncio
async def multi_coro():
async with asyncio.TaskGroup() as tg:
tg.create_task(coro('5'))
print('3-')
tg.create_task(coro('6'))
print('4-')
async def coro(i):
print(i)
await asyncio.sleep(.1)
print(i)
async def main():
async with asyncio.TaskGroup() as tg:
tg.create_task(multi_coro())
print('1-')
# here we end a task group, forcing the created tasks
# to run up to completion.
async with asyncio.TaskGroup() as tg:
tg.create_task(coro('7'))
print('2-')
print('8-')
This won't print your desired order - it is deterministically "3 4 (sync) 5 6 5 6 (both tasks in first group, alternating as they find the sleep (.1) 1 2 (sync) 7 7 (new task) 8 (sync) " - but understanding this, you have the keys to enforce the particular order you need.
Keep in mind a task group is a normal object you can just pass along as a parameter - so, instead of creating a new task group inside "multi_coro" you can pass it tg
as an argument, and the loop will await for any tasks it creates there along with others:
import asyncio
async def multi_coro(tg):
tg.create_task(coro('5'))
print('3-')
tg.create_task(coro('6'))
print('4-')
async def coro(i):
print(i)
await asyncio.sleep(.1)
print(i)
async def main():
async with asyncio.TaskGroup() as tg:
tg.create_task(multi_coro(tg))
tg.create_task(coro(7))
async with asyncio.TaskGroup() as tg:
print('1-')
tg.create_task(coro('8'))
print('2-')
print('9-')
asyncio.run(main())
printing:
3-
4-
7
5
6
7
5
6
1-
2-
8
8
9-
Upvotes: 1