[英]Using nested asyncio.gather() inside another asyncio.gather()
我有一個具有各種方法的課程。 我在該類中有一個方法,例如:
class MyClass:
async def master_method(self):
tasks = [self.sub_method() for _ in range(10)]
results = await asyncio.gather(*tasks)
async def sub_method(self):
subtasks = [self.my_task() for _ in range(10)]
results = await asyncio.gather(*subtasks)
async def my_task(self):
return "task done"
所以這里的問題是:
在從另一個asyncio.gather()
調用的協程中使用asyncio.gather()
是否有任何問題、優點/缺點? 任何性能問題?
asyncio
循環是否以相同的優先級處理所有級別的所有任務? 這會給出相同的性能,如果我呼吁所有的協程從單一asyncio.gather() master_method
?
TLDR:使用gather
而不是返回任務簡化了使用並使代碼更易於維護。 雖然gather
有一些開銷,但對於任何實際應用來說都是可以忽略不計的。
gather
? 在退出協程之前gather
子任務的要點是延遲協程的完成,直到其子任務完成。 這封裝了實現,並確保協程作為一個單一的實體“做它的事”。
另一種方法是return
子任務,並期望調用者將它們運行到完成。
為簡單起見,讓我們看一個單層——對應於中間的sub_method
但有不同的變化。
async def child(i):
await asyncio.sleep(0.2) # some non-trivial payload
print("child", i, "done")
async def encapsulated() -> None:
await asyncio.sleep(0.1) # some preparation work
children = [child() for _ in range(10)]
await asyncio.gather(*children)
async def task_children() -> 'List[asyncio.Task]':
await asyncio.sleep(0.1) # some preparation work
children = [asyncio.create_task(child()) for _ in range(10)]
return children
async def coro_children() -> 'List[Awaitable[None]]':
await asyncio.sleep(0.1) # some preparation work
children = [child() for _ in range(10)]
return children
所有encapsulated
、 task_children
和coro_children
以某種方式編碼存在子任務。 這允許調用者以可靠地“完成”實際目標的方式運行它們。 但是,每個變體的不同之處在於它自己做了多少以及調用者必須做多少:
encapsulated
是“最重”的變體:所有子項都在Task
中運行,並且有一個額外的gather
。 但是,調用者不會暴露於任何以下內容: await encapsulated()
這保證了功能按預期工作,並且可以自由更改其實現。task_children
是中間變體:所有孩子都在Task
中運行。 調用者可以決定是否以及如何等待完成: tasks = await task_children() await asyncio.gather(*tasks) # can add other tasks here as well
這保證了功能按預期啟動。 但是,它的完成依賴於調用者具有一些知識。coro_children
是“最輕的”變體:實際上沒有任何孩子在運行。 調用者負責整個生命周期: tasks = await coro_children() # children don't actually run yet! await asyncio.gather(*tasks) # can add other tasks here as well
這完全依賴於調用者來啟動和等待子任務。 使用encapsulated
模式是一個安全的默認設置——它確保協程“正常工作”。 值得注意的是,使用內部gather
的協程仍然看起來像任何其他協程。
gather
? gather
實用程序 a) 確保其參數作為Task
運行,並且 b) 提供一個Future
,一旦任務完成就會觸發。 由於gather
通常是用來當一個人將運行參數作為Task
小號反正有從這個沒有額外的開銷; 同樣,這些是常規Task
並且與其他所有內容具有相同的性能/優先級特征¹。
唯一的開銷來自包裝Future
; 這負責記賬(確保參數是任務),然后只等待,即什么都不做。 在我的機器上, 測量開銷表明它平均花費的時間是運行 no-op Task
兩倍。 對於任何現實世界的任務,這本身應該已經可以忽略不計。
此外, gather
子任務的模式本質上意味着存在一個gather
節點樹。 因此, gather
節點的數量通常遠低於任務數量。 例如,對於每個gather
10 個任務的情況,總共只需要 11 個gather
即可處理總共 100 個任務。
master_method 0
sub_method 0 1 2 3 4 5 ...
my_task 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 ...
¹也就是說,沒有。 asyncio
目前沒有Task
優先級的概念。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.