Tokio[1]的 task (一個 Future ) 里如果使用了阻塞調用,例如std::Mutex
,會阻塞當前的 tokio-worker 線程,這個 worker 無法再執行其他 task。所以代碼里如果不可避免的有(少量的)阻塞調用,就要為 runtime 啟動更多的 worker 線程,保證存在沒被阻塞的 worker 來執行待調度的 task,以避免整個 tokio runtime 完全 hang 住(有 task 但沒 worker 運行它)。
但現實是,就算 worker 再多,tokio 也可能造成永久性的阻塞。
原因是 tokio 里的待執行 task 不是簡單的放到一個 queue 里,除了 runtime 內共享的,可被每個 worker 消費的run_queue
[2],每個 worker 還有一個自己的lifo_slot
[3],只存儲一個最后被放入的 task (目的是減小調度延遲)。lifo_slot
只由它所屬的 worker 使用,里面存儲的 task 不能被其他 worker 執行。由于這個結構,構造 hang 住的方法是如圖所示:
-
?Future f1 被 runtime-1 執行, 持有一個 async 的鎖
m
后,返回了Pending
,這時它被調度到 worker-1 本地的lifo_slot
。 -
?Future f2 在 runtime-1 執行后返回
Pending
,被放入共享隊列run_queue
。 -
?Future f3 在 runtime-1 中執行, 它將一個任務
f4
交給其他的 runtime 去完成(例如為了隔離網絡IO和本地磁盤IO),使用block_on(f4)
[4]的方式,等待執行結果返回。 -
? f4 中也需要鎖
m
,等待。
這時,f2 在共享隊列run_queue
中,可以被執行,但是 f1 在 worker-1 本地的lifo_slot
里,只能由 worker-1 調度,但 worker-1 當前阻塞在 f3。于是等待關系形成了一個環:f4 → m(f1) → f3 → f4
,hang 死任務達成。
審核編輯 :李倩
-
存儲
+關注
關注
13文章
4323瀏覽量
85927 -
線程
+關注
關注
0文章
505瀏覽量
19703 -
Worker
+關注
關注
0文章
8瀏覽量
6472 -
Tokio
+關注
關注
0文章
12瀏覽量
65
原文標題:Tokio 中 hang 死所有 worker 的方法
文章出處:【微信號:Rust語言中文社區,微信公眾號:Rust語言中文社區】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論