[C#] .Result、async void、迴圈 await 的真實後果
在 C# 裡,async/await 大概是大家寫最多,但理解最少的語法。
很多人使用 async,只是因為 "不加就跳警告,所以我加" , 至少我有時候是因為這樣
尤其是在試跑 open soruce 的專案
如果你也是這樣,那可能要稍微停一下,async/await 不是什麼魔法關鍵字
用錯不但不會變快,還可能讓效能更差、ThreadPool 卡死,甚至造成 deadlock
接下來我把 async/await 拆開來講
講底層運作、真實陷阱、什麼時候該用、什麼時候不要用
1. 非同步到底是什麼?一句話講完 : 非同步不是讓程式"跑得更快",非同步是讓你"不要塞住"
當你在做 I/O(API call、畫面 render)時,執行緒完全沒事做。
讓它卡住等三秒是浪費,所以 async 的設計就是
I/O 還沒回來 → 請你先去忙別的
I/O 回來 → 回來把剩下的行程跑完
完全不是多執行緒,而是"把CPU借出去"
async/await 底層其實超複雜,所以如果你專案要全面改寫你只會想說句 WTH
編譯器看到 await 時做的事,比你想像中多:
-先生成一個 state machine
-把方法拆成好幾段
-遇到還沒完成的 Task,執行緒先被釋放
-Task 完成後,用 callback 接續跑下一段
最後你只看到:
2. 常見錯誤: .Result / .Wait() 導致 deadlock
或是
就在製造 deadlock 的可能性。
因為這兩個 API 會把執行緒整個堵住,而 async 方法的 continuation 剛好也想回到這條執行緒上執行。
結果兩邊互等
正確寫法
3.少用或不要 async void ,寫成 async void 的方法,基本上你就失去控制了
更改寫法
4. 大量 CPU 工作使用 async ,因為有時候這需要實測判斷,如果發現他是大量 CPU 運作
你加上了有時候不會比較好,建議使用 Parallel.For、SIMD 等平行化方式處理
正確做法
5. 在迴圈裡連續 await
這是 async 最常寫出的反效果程式片段
這邊問題會導致互卡
較正確做法
做個結論 -
如果你先弄懂 async 的核心概念(重點是不阻塞),也知道它背後其實是靠 state machine 在切換流程
再避開幾個常見雷(例如不要用 .Result、不要寫 async void、也不要在迴圈裡每次都 await)
那 async/await 真的能讓你的程式跑得更順、更有效率,也能處理更多 I/O 工作,維護起來也不會那麼痛苦
但如果你只是為了把警告消掉才加 async,那最後通常只會越寫越亂。要把 async 用好,還是得先理解它的運作方式
