devxvda1
devxvda1

Reputation: 462

Python - How to make tasks run asynchronously using asyncio

I was trying to run 2 tasks at the same time for a single client from the list but it doesn't work as expected - the tasks still ran synchronously (please see Output). Can you help me understand what's wrong with my code? Thank you.

Code

#!/usr/bin/env python3

import asyncio


async def do_b(client):
    print('{}: doing b...'.format(client))
    await asyncio.sleep(1)
    print('{}: b is done!'.format(client))


async def do_a(client):
    print('{}: doing a...'.format(client))
    await asyncio.sleep(1)
    print('{}: a is done!'.format(client))


async def loop_a():
    clients = ['CLIENT-1', 'CLIENT-2', 'CLIENT-3', 'CLIENT-4', 'CLIENT-5']

    for client in clients:
        task1 = asyncio.ensure_future(do_a(client))
        task2 = asyncio.ensure_future(do_b(client))

        await asyncio.gather(task1, task2)


if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    loop.run_until_complete(loop_a())
    loop.close()

Output

CLIENT-1: doing a...
CLIENT-1: doing b...
CLIENT-1: a is done!
CLIENT-1: b is done!
CLIENT-2: doing a...
CLIENT-2: doing b...
CLIENT-2: a is done!
CLIENT-2: b is done!
CLIENT-3: doing a...
CLIENT-3: doing b...
CLIENT-3: a is done!
CLIENT-3: b is done!
CLIENT-4: doing a...
CLIENT-4: doing b...
CLIENT-4: a is done!
CLIENT-4: b is done!
CLIENT-5: doing a...
CLIENT-5: doing b...
CLIENT-5: a is done!
CLIENT-5: b is done!

EDIT:

Another question. How do I make it execute like this which does not follow their order in the list? Because currently, CLIENT-1 is being blocked by CLIENT-2, same as the other clients and I was trying to avoid that. Is this possible?

CLIENT-1: doing a...
CLIENT-2: doing a...
CLIENT-1: doing b...
CLIENT-3: doing a...
CLIENT-1: a is done!
CLIENT-1: b is done!
CLIENT-2: a is done!
CLIENT-3: a is done!
CLIENT-2: doing b...
CLIENT-2: b is done!
CLIENT-3: doing b...
CLIENT-3: b is done!

Upvotes: 0

Views: 493

Answers (2)

zvone
zvone

Reputation: 19332

a and b are running at the same time and the output shows that. Clients 1, 2, 3, 4, and 5 are running sequentially because there is an await asyncio.gather between them.

You can merge all of the tasks into one list to make them run at the same time:

async def loop_a():
    clients = ['CLIENT-1', 'CLIENT-2', 'CLIENT-3', 'CLIENT-4', 'CLIENT-5']

    tasks = []
    for client in clients:
        tasks.append(asyncio.ensure_future(do_a(client)))
        tasks.append(asyncio.ensure_future(do_b(client)))
    await asyncio.gather(*tasks)

Now it prints:

CLIENT-1: doing a...
CLIENT-1: doing b...
CLIENT-2: doing a...
CLIENT-2: doing b...
CLIENT-3: doing a...
CLIENT-3: doing b...
CLIENT-4: doing a...
CLIENT-4: doing b...
CLIENT-5: doing a...
CLIENT-5: doing b...
CLIENT-1: a is done!
CLIENT-2: a is done!
CLIENT-3: b is done!
CLIENT-1: b is done!
CLIENT-3: a is done!
CLIENT-2: b is done!
CLIENT-4: a is done!
CLIENT-5: b is done!
CLIENT-4: b is done!
CLIENT-5: a is done!

Upvotes: 0

abarnert
abarnert

Reputation: 365597

First, the two tasks aren't running synchronously, as you can see from the fact that doing b... keeps showing up in between doing a... and a is done!.

Each pair of tasks is finishing before the next client starts, but that's because you're explicitly asking for that to happen. If you don't want to await each pair, just… don't await each pair. For example:

futs = []
for client in clients:
    futs.append(asyncio.ensure_future(do_a(client)))
    futs.append(asyncio.ensure_future(do_b(client)))
await asyncio.gather(*futs)

Upvotes: 1

Related Questions