在 C# WinForms 開發中,常遇到這樣的場景:主程式透過 Socket 協議接收資料指令,收到特定指令後需要啟動 Timer 控件執行定時任務。然而開發者發現,在 Socket 回調執行緒中直接啟用 timer1.Enabled = true 後,Timer 的 Tick 事件卻不再觸發。若改用 System.Timers.Timer 又發現難以控制 UI 更新。這究竟是為什麼?
WinForms 的 System.Windows.Forms.Timer 元件是基於 UI 執行緒的訊息迴圈運作,它不支援跨執行緒直接操作。Socket 的非同步接收回調通常運行在執行緒池的背景執行緒上,若在該執行緒直接設定 timer.Enabled = true,Timer 內部無法正確掛鉤到 UI 訊息泵,導致 Tick 事件不被引發。
將 Timer 的啟用邏輯封裝在委派中,透過 Control.Invoke 或 BeginInvoke 送回 UI 執行緒執行:
this.Invoke(new Action(() => {
timer1.Enabled = true;
}));
這樣 Timer 就能在 UI 執行緒正確初始化並開始計時,後續的 Tick 事件也會在 UI 執行緒觸發,安全更新介面元素。
System.Timers.Timer 的 Elapsed 事件同樣在執行緒池執行,若在其中直接操作 UI 控制項,仍需手動 Invoke,且易造成競態條件。相比之下,WinForms Timer 專為 UI 設計,配合 Invoke 使用最簡潔可靠。
Invoke 將 Timer 控制代碼封送至 UI 執行緒。System.Threading.Timer 搭配 SynchronizationContext,但 UI 更新仍須回主執行緒。掌握跨執行緒 UI 操作原理,是 WinForms 開發避坑的關鍵。