WinForms 非同步必學:用 async/await 擺脫 UI 卡頓

| CSharp | 3 Reads

背景

在按鈕事件中若直接執行同步 I/O(如 ToList())或耗時計算,會鎖死 UI,畫面無法拖曳、重繪,甚至被系統標示為「(無回應)」。


1. await 的核心作用

  1. 分段執行
    遇到 await 時,方法會將後續程式拆成 callback,先把控制權還給 WinForms 的訊息迴圈。

  2. 保證順序
    await,後續程式碼僅在被 await 的 Task 完成後才執行;若忽略 await,便可能「fire-and-forget」。

  3. 不一定開新執行緒
    非同步 I/O(如 ToListAsync())本身即不阻塞;若需背景 CPU 密集,可採 await Task.Run(...)


2. UI 不鎖死的好處

  • 持續回應:視窗可拖曳、最小化、重繪,不會顯示「無回應」。

  • 事件排隊:使用者在 await 期間點擊其他按鈕,該事件入隊,待當前非同步完成並恢復後再執行。

  • 進度與取消:可在等待時更新進度條或實作「取消」機制,提升使用者體驗。


3. 簡易範例

private async void btnProcess_Click(object sender, EventArgs e)
{
    CollectProcessInput();

    // 非同步取資料,UI 不鎖死
    var list = await context.Entities.ToListAsync();

    // 確保資料拿到後才執行
    UpdateUI(list);
}

4. 實作建議

  • I/O 密集:優先使用框架提供的非同步 API。

  • CPU 密集:包裝於 Task.Run(...);並以 await 等待結果。

  • 落實 await:確保所有需要等待的非同步呼叫前皆加 await,避免執行順序錯亂。


透過 async/await,你可以用近似同步的程式流程,確保順序執行非同步邏輯,同時維持 WinForms UI 的流暢與回應性。

This article was last edited at