非同步作業完成後,必須將結果安全地傳回 UI 執行緒更新介面。本文系統解析 .NET 三代執行緒通訊機制:ISynchronizeInvoke (1.0)、SynchronizationContext (2.0)、AsyncOperation/AsyncOperationManager (2.0+),並給出 WinForm 與 Console 完整實戰範例。
WinForm 控制項具有執行緒親和性,背景執行緒直接設定 Label.Text 會拋出 InvalidOperationException:「執行緒間操作無效」。
InvokeRequired:判斷呼叫端是否為 UI 執行緒。Invoke(delegate, args):同步封送,阻塞呼叫端直到 UI 執行緒完成。BeginInvoke/EndInvoke:非同步封送,不阻塞呼叫端。ISynchronizeInvoke sync = this.listBox1;
if (sync.InvokeRequired)
sync.Invoke(new Action<string>(AddLog), "訊息");
else
AddLog("訊息");
WinForm 啟動時自動建立 WindowsFormsSynchronizationContext;Console 需手動 new SynchronizationContext()。兩大核心方法:
Send(SendOrPostCallback, state):同步 (對應 Invoke)。Post(SendOrPostCallback, state):非同步,內部使用 ThreadPool.QueueUserWorkItem (對應 BeginInvoke)。// UI 執行緒取得 context
SynchronizationContext ctx = SynchronizationContext.Current;
DoWork.BeginInvoke(OnComplete, ctx);
// 背景執行緒回呼
void OnComplete(IAsyncResult ar)
{
SynchronizationContext ctx = (SynchronizationContext)ar.AsyncState;
ctx.Post(_ => listBox.Items.Add("完成"), null);
}
var ctx = new SynchronizationContext();
new Thread(o => {
var ui = (SynchronizationContext)o;
ui.Post(_ => Console.WriteLine("來自背景執行緒"), null);
}).Start(ctx);
封裝 SynchronizationContext,自動捕獲建立時的 context,提供 Post 與 OperationCompleted (通知完成,供元件設計師追蹤)。
public class Worker
{
private AsyncOperation op = AsyncOperationManager.CreateOperation(null);
public event EventHandler WorkDone;
public void Start()
{
new Thread(DoWork).Start();
}
private void DoWork()
{
op.Post(_ => WorkDone?.Invoke(this, EventArgs.Empty), null);
op.OperationCompleted(); // 重要:通知操作結束
}
}
| 機制 | 適用場景 | 優點 | 缺點 |
|---|---|---|---|
| ISynchronizeInvoke | WinForm 快速修補 | 直接用 Control.Invoke | 緊耦合 UI、無法單元測試 |
| SynchronizationContext | 跨平台、庫開發 | 抽象化、支援 Console/ASP.NET/WPF | 手動傳遞 context |
| AsyncOperation | 元件開發、BackgroundWorker 替代 | 自動捕獲 context、支援取消/進度 | 稍顯繁瑣 |

建議:新專案優先使用 SynchronizationContext 或 Task/async-await (現代模式);維護舊代碼時理解三代演進有助於正確重構。