[英]What scheduling algorithm does asyncio's event loop use?
我查看了所有文檔,但找不到它。
假設完成了多個await
調用,並且它們的協程都准備好恢復,那么asyncio
的事件循環使用什么算法來決定恢復哪個協程?
1) 文檔向我們展示了事件循環以run_forever
/ run_until_complete
開始。 我們需要找到函數的源代碼,看看接下來會發生什么。
2)最快的方法(我知道)是在github上搜索相關名稱。 轉到github.com/python/cpython ,使用左上角的搜索表單:
Github 將向您顯示項目中的所有事件。 確保在代碼中搜索:
3)我們需要實現,找到了其中的兩個: 一個在 ProactorEventLoop 內部,沒有發生任何有趣的事情,因為它部分地重新實現了父事件循環; 和BaseEventLoop 里面的一個,它似乎是我們要搜索的。
4)讓我們檢查一下代碼。 很快我們就會看到,一切都在while True
循環中調用_run_once
:
try:
events._set_running_loop(self)
while True:
self._run_once()
if self._stopping:
break
5)讓我們去_run_once
(我們可以在頁面上搜索def _run_once
)。 這是恢復計划的事情的地方。 通過源代碼,您會看到兩個有趣的地方:
event_list = self._selector.select(timeout)
-這是 asyncio 休眠的地方,直到套接字上的活動。 您可以進一步閱讀文檔來研究此機制。 但是這個地方本身並沒有恢復協程。This is the only place where callbacks are actually *called*. All other places just add them to ready.
This is the only place where callbacks are actually *called*. All other places just add them to ready.
正是我們搜索的內容。 您可以在那里閱讀源代碼(通常在_run_once
函數內部)以查看回調是如何執行的,以及它們是如何添加到self._ready
以執行的。 回調包括直接添加的回調和 asyncio 在內部用於恢復協程的回調。
您還可以重新實現事件循環來使用它。 看看這里的代碼,它包含事件循環重新實現的示例。
幾點注意事項:
BaseEventLoop
中使用一個,但其他事件循環(如自定義uvloop)可能會做一些不同的事情。如果您使用默認事件循環,python 3.10 中的 TLDR 准備恢復最長的協程將繼續。 但是,每次執行事件循環時只會檢查一次計時器(可能還有 io 事件)。
在內部BaseEventLoop
保持一個雙端隊列self._ready
包含所有准備好被調用的句柄。
句柄並不完全是協程,但與之相關。 實際上,asyncio.run 將協程包裝在任務中,任務基本上將協程拆分為多個步驟,其中一個步驟是需要執行的所有內容,以到達協程中的下一個點,將控制權交還給事件循環(這可能晚於下一個等待語句,因為並非所有等待語句都將控制權交還給事件循環)。 句柄只是 asyncio 引用需要執行的步驟的內部方式。
句柄的執行順序與它們放在_ready
雙端隊列上的順序相同。 此順序取決於您的系統,並且在 Unix 上每次事件循環的迭代如下:
第一個。 自事件循環的上一次迭代以來,所有非 io、非基於計時器的句柄都按照這些准備就緒的順序准備就緒。
第二。 所有基於 io 的句柄都按照它們在上一次迭代准備就緒后由 Unix select 系統調用返回的順序排列。
第三。 所有基於計時器的句柄,其計時器已按順序用完。
我沒有檢查,但我相信在 Windows 上使用基於前攝器的事件循環時,您會發現上面的第 1 點和第 2 點將按照它們准備好的順序交錯。
Mikhail Gerasimov已經給出的詳細答案解釋了如何找出標准事件循環在任何給定 python 版本中所做的事情。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.