簡體   English   中英

asyncio.Queue 被 1 個協程添加到隊列中,1 個協程從隊列中獲取

[英]asyncio.Queue Stuck With 1 Coroutine Adding to Queue, 1 Coroutine Getting from Queue

在我下面的簡單 asyncio 代碼中,應用程序有一個任務self.add_item_loop_task不斷將asyncio.Queue添加到名為self.queue的 asyncio.Queue 中,而第二個任務self.get_item_loop_task不斷等待將某些內容添加到隊列中並將其print出來.

但是,當我運行這個應用程序時,它只會打印一次0 ,然后就卡在那里了。 我相信self.get_item_loop_task中的循環沒有進行。 為什么會這樣?

import asyncio

class App:
    def __init__(self):
        self.queue = asyncio.Queue()

    async def start(self):
        self.add_item_loop_task = asyncio.create_task(self.add_item_loop())
        self.get_item_loop_task = asyncio.create_task(self.get_item_loop())
        await asyncio.wait(
            [
                self.add_item_loop_task,
                self.get_item_loop_task,
            ]
        )

    async def stop(self):
        self.add_item_loop_task.cancel()
        self.get_item_loop_task.cancel()

    async def add_item_loop(self):
        i = 0
        while True:
            await self.queue.put(i)
            i += 1
            await asyncio.sleep(1)

    async def get_item_loop(self):
        while True:
            item = await self.queue.get()
            print(item)


app = App()
try:
    asyncio.run(app.start())
except KeyboardInterrupt:
    asyncio.run(app.stop())

這是由 asyncio 的一些可疑的實現細節引起的。 當您說self.queue = asyncio.Queue()時,如果尚不存在事件循環,這實際上將創建一個事件循環。 同時,當您調用asyncio.run()時,它總是會創建一個新的事件循環。 這意味着如果您在調用asyncio.run()之前創建一個隊列,您可能會遇到一些奇怪的行為,因為有兩個事件循環,一個是您的隊列使用的,一個是asyncio.run正在使用的。

您可以通過將App的創建移動到您傳遞給asyncio.run()的協程 function 來解決此問題,如下所示。 這樣做您的應用程序將按預期工作。

async def main():
    app = App()
    await app.start()

asyncio.run(main())

正如@Matt Fowler 解釋的那樣,代碼正在創建兩個同時發生的事件循環。 一個在初始化 class 時創建,第二個在運行asyncio.run(main())時創建。

您可以簡單地將self.queue = asyncio.Queue()def __init__(self)方法移動到開始的async def start(self)方法。 新創建的asyncio.Queue() object 將可用於這兩個任務。

以下代碼有效:

import asyncio

class App:
# def __init__(self):
#     self.queue = asyncio.Queue()

async def start(self):
    # self.queue in the following line will be available to both tasks
    self.queue = asyncio.Queue()
    self.add_item_loop_task = asyncio.create_task(self.add_item_loop())
    self.get_item_loop_task = asyncio.create_task(self.get_item_loop())
    await asyncio.wait(
        [
            self.add_item_loop_task,
            self.get_item_loop_task,
        ]
    )

async def stop(self):
    self.add_item_loop_task.cancel()
    self.get_item_loop_task.cancel()

async def add_item_loop(self):
    i = 0
    while True:
        await self.queue.put(i)
        i += 1
        await asyncio.sleep(1)

async def get_item_loop(self):
    while True:
        item = await self.queue.get()
        print(item)

app = App()
try:
  asyncio.run(app.start())
except KeyboardInterrupt:
  asyncio.run(app.stop())

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM