Python Forum
Asyncio: Queue consumer gets out of while loop without break. Where exactly and how?
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Asyncio: Queue consumer gets out of while loop without break. Where exactly and how?
#1
I have the following code:
import asyncio

PARALLEL_DOWNLOADS = 4
names = ['Aris', 'Kostas', 'Nikos', 'Makis', 'Christos', 'Andreas', 'Vasilis', 'Thanasis', 'Petros', 'Pavlos']

async def say_hello(q):
    while True:
        name = await q.get()
        print('Hello {}'.format(name))
        q.task_done()
        print('After task done')
        
async def main():
    q = asyncio.Queue()

    for name in names:
        q.put_nowait(name)

    workers = []
    for _ in range(PARALLEL_DOWNLOADS):
        worker = asyncio.create_task(say_hello(q))
        workers.append(worker)

    await q.join()
    for worker in workers:
        worker.cancel()
    await asyncio.gather(*workers, return_exceptions=True)
    
asyncio.run(main())
Here when all the queue data has been consumed the while loop in the say_hello function breaks at the end of the loop. And this without any break, continue or return statement! I would say "automagically".
OK, this is the wanted result for me but i can't understand how asyncio does this. At which exact point of the code the while loop "breaks", or yields or whatever?
Maybe at "name = await q.get()" it sees that there is nothing else in the queue and yields (or returns) from the say_hello coroutine function?
Reply
#2
An exception is raised by "name = await q.get()". It is a very quiet exception, but it kicks you out of say_hello().

If I remove return_exceptions=True from this line the exception gets a lot less quiet. :
    await asyncio.gather(*workers)
Error:
Traceback (most recent call last): File "C:\Python\Python39\lib\asyncio\queues.py", line 166, in get await getter asyncio.exceptions.CancelledError During handling of the above exception, another exception occurred: Traceback (most recent call last): File "myprogram", line 23, in main await asyncio.gather(*workers) asyncio.exceptions.CancelledError During handling of the above exception, another exception occurred: Traceback (most recent call last): File "myprogram", line 25, in <module> asyncio.run(main()) File "C:\Python\Python39\lib\asyncio\runners.py", line 44, in run return loop.run_until_complete(main) File "C:\Python\Python39\lib\asyncio\base_events.py", line 642, in run_until_complete return future.result() asyncio.exceptions.CancelledError
Quote:exception asyncio.CancelledError
The operation has been cancelled.

This exception can be caught to perform custom operations when asyncio Tasks are cancelled. In almost all situations the exception must be re-raised.
Here I will catch the cancelled error so I can perform some important cleanup.
import asyncio
names = ['Aris', 'Kostas', 'Nikos', 'Makis', 'Christos', 'Andreas', 'Vasilis', 'Thanasis', 'Petros', 'Pavlos']

async def say_hello(id, q):
    while True:
        try:
            name = await q.get()
            print(f'{id} {name}')
            await asyncio.sleep(0.1)
            q.task_done()
        except asyncio.CancelledError as msg:
            print(msg)
            break
    print(f"{id} cancelled")

async def main():
    q = asyncio.Queue()

    for name in names:
        q.put_nowait(name)

    workers = [asyncio.create_task(say_hello(i, q)) for i in range(4)]

    await q.join()
    for worker in workers:
        worker.cancel()

    await asyncio.gather(*workers)

asyncio.run(main())
Output:
0 Aris 1 Kostas 2 Nikos 3 Makis 0 Christos 2 Andreas 1 Vasilis 3 Thanasis 0 Petros 1 Pavlos 0 cancelled 1 cancelled 2 cancelled 3 cancelled
Wow, cancelled without even leaving behind a note. That's cold.
Reply
#3
Oh!!! Thank you very much! Very good explaination for such a complex module Smile
So the eventloop breaks and this raises the CancelledError in the coroutine! Thak you!
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  Code won't break While loop or go back to the input? MrKnd94 2 944 Oct-26-2022, 10:10 AM
Last Post: Larz60+
  How to break out of a for loop on button press? philipbergwerf 6 1,732 Oct-06-2022, 03:12 PM
Last Post: philipbergwerf
  break out of for loop? User3000 3 1,439 May-17-2022, 10:18 AM
Last Post: User3000
  Clearing an asyncio queue if it's empty H84Gabor 0 1,804 Jan-28-2022, 02:27 AM
Last Post: H84Gabor
  tkinter control break a while loop samtal 0 2,387 Apr-29-2021, 08:26 AM
Last Post: samtal
  Cannot 'break' from a "for" loop in a right place tester_V 9 3,961 Feb-17-2021, 01:03 AM
Last Post: tester_V
  Reliable rabbitmq consumer kashcode 1 1,568 Dec-18-2020, 02:37 PM
Last Post: ndc85430
  How to break a loop in this case? Blainexi 10 7,254 Sep-24-2020, 04:06 PM
Last Post: Blainexi
  task queue Valon1981 8 3,592 Jul-07-2020, 07:41 AM
Last Post: freeman
  how to break the loop? bntayfur 8 3,055 Jun-07-2020, 11:07 PM
Last Post: bntayfur

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020