0

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.

11
  • So quick question, is async required? Cause async does not guarantee the order by which code is run. Commented Jan 20, 2023 at 16:22
  • multi_coro and coro are interwined. Commented Jan 20, 2023 at 16:36
  • @TeddyBearSuicide yes async is required. coro represents a function with async interaction(HTTP requests). Commented Jan 20, 2023 at 17:22
  • @RomanPerekhrest yes. It's not an error, I would like to prioritized multi_coro execution Commented Jan 20, 2023 at 17:23
  • So are you just wanting multi_coro to complete first before calling coro? Or another way is that you want to call a coroutine and once its finished call the second coroutine? Commented Jan 20, 2023 at 19:32

1 Answer 1

1

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-
Sign up to request clarification or add additional context in comments.

4 Comments

Thanks you very much for your explanation. In my case, I had multiple similar coroutines(here 'coro') which need to call two others coroutines if a condition is verified(here 'multi_coro'). This is what I wanted to replicate in the simple example above. So I would like to iterate my loop with similar coroutines('coro'). And if I want to start two others coroutines('multi_coro') in a couritine('coro'). I would like to add in my loop the new coroutines created. Without waiting for it to execute a first time completely my loop.
With your explanations, I could iterate my coroutines and if my condition is fulfilled, I break the group of tasks to execute the couroutines as fast as possible. Then recreate my tasks and start the scenario again. But isn't there a cleaner way than breaking the task group to schedule the execution of a task group immediately? I can give you another more complete example to illustrate my point if needed!
as for breaking a group of tasks, fixing something and rescheduling them - something there smells like you need asyncio.wait - which can "pause" a set of tasks on an exception or on a timeout: docs.python.org/3/library/asyncio-task.html#asyncio.wait - maybe there is some possible clever interation possible with task groups + asyncio.wait . feel free to write another question, if needed, and ping me (e-mail on profile)
Hello, I have sent you an email

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.