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