對於asyncio的隊列,使用get()時就已經把隊列中的元素彈出了,而task_done()只是減少未完成任務數量。
這是我找到的相關資料(雖然是線程的隊列,但協程的應該也同理):
https://stackoverflow.com/questions/1593299/python-queue-get-task-done-issue/1593330#1593330
No, queue.get() pops the item off the queue. After you do that, you can do whatever you want with it, as long as the producer works like it should and doesn’t touch it anymore. queue.task_done() is called only to notify the queue that you are done with something (it doesn’t even know about the specific item, it just counts unfinished items in the queue), so that queue.join() knows the work is finished.
之後,我實踐了一下。
>>> import asyncio
>>> queue=asyncio.Queue(5)
>>> async def sb():
while 1:
await queue.put(1)
print(queue)
>>> async def cnm():
while 1:
await queue.get()
print(queue)
queue.task_done()
print(queue)
>>> async def main():
task=asyncio.create_task(sb())
await asyncio.sleep(5)
task.cancel()
asyncio.create_task(cnm())
>>> asyncio.run(main())
<Queue maxsize=5 _queue=[1] tasks=1>
<Queue maxsize=5 _queue=[1, 1] tasks=2>
<Queue maxsize=5 _queue=[1, 1, 1] tasks=3>
<Queue maxsize=5 _queue=[1, 1, 1, 1] tasks=4>
<Queue maxsize=5 _queue=[1, 1, 1, 1, 1] tasks=5>
<Queue maxsize=5 _queue=[1, 1, 1, 1] tasks=5>
<Queue maxsize=5 _queue=[1, 1, 1, 1] tasks=4>
<Queue maxsize=5 _queue=[1, 1, 1] tasks=4>
<Queue maxsize=5 _queue=[1, 1, 1] tasks=3>
<Queue maxsize=5 _queue=[1, 1] tasks=3>
<Queue maxsize=5 _queue=[1, 1] tasks=2>
<Queue maxsize=5 _queue=[1] tasks=2>
<Queue maxsize=5 _queue=[1] tasks=1>
<Queue maxsize=5 tasks=1>
<Queue maxsize=5>
可以看到,使用get()後,隊列中的元素減少了,但是tasks沒有減少,使用task_done()後tasks才減少。
查看queue的屬性:
猜測_unfinished_tasks是上面的tasks。
驗證:
>>> async def cnm():
while 1:
await queue.get()
print(queue._unfinished_tasks)
queue.task_done()
print(queue._unfinished_tasks)
>>> asyncio.run(main())
<Queue maxsize=5 _queue=[1] tasks=1>
<Queue maxsize=5 _queue=[1, 1] tasks=2>
<Queue maxsize=5 _queue=[1, 1, 1] tasks=3>
<Queue maxsize=5 _queue=[1, 1, 1, 1] tasks=4>
<Queue maxsize=5 _queue=[1, 1, 1, 1, 1] tasks=5>
5
4
4
3
3
2
2
1
1
0
猜測正確。
因此,如果不從隊列中取出值而直接用task_done()的話會使隊列中還有元素,但是未完成的任務數量為0。
>>> queue.put_nowait(2)
>>> queue
<Queue at 0x24cf9965610 maxsize=5 _queue=[2] tasks=1>
>>> queue.task_done()
>>> queue
<Queue at 0x24cf9965610 maxsize=5 _queue=[2]>