Hadevmin
Hadevmin

Reputation: 21

Asyncio: order loop

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

Answers (1)

jsbueno
jsbueno

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

Related Questions