首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> 關於.NET

.NET Compact Framework 多執行緒 UI 非同步更新模式 (事件 + Invoke)

     編輯:關於.NET

場景:背景執行緒需通知 UI 更新

Windows Mobile / WinCE 開發常見需求:網路連線管理 (WiFi/GPRS/3G) 由 Worker Thread 負責,連線狀態變更時需通知 UI Thread 更新介面。直接在 Worker Thread 操作 UI 控制項會拋出 Cross-thread operation not valid 例外,因 UI 元素非執行緒安全。

解決模式:事件訂閱 + Control.Invoke

  1. Worker Thread 定義事件與委派
  2. UI Thread 訂閱事件
  3. 事件觸發時,UI Thread 透過 Invoke 將更新動作排程到 UI 執行緒

完整實作範例

1. Worker Thread 類別 (ConnectionMgr)

public class ConnectionMgr
{
    // 單例模式 (簡化示範)
    private static readonly ConnectionMgr _instance = new ConnectionMgr();
    public static ConnectionMgr Instance => _instance;

    // 事件委派定義
    public delegate void MessageEventHandler(string message);
    public event MessageEventHandler MessageEvent;

    // 觸發事件的保護方法 (執行緒安全)
    private void OnMessage(string message)
    {
        // 取得事件副本避免競態條件
        MessageEventHandler handler = MessageEvent;
        handler?.Invoke(message);
    }

    // 模擬連線狀態變更
    private void ConnectHandler()   => OnMessage("Connected");
    private void DisconnectHandler() => OnMessage("Disconnected");

    // 對外公開的控制方法
    public void SimulateConnect()   => ConnectHandler();
    public void SimulateDisconnect() => DisconnectHandler();
}

2. UI Thread 表單 (Form1)

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
        // 建構函式訂閱事件
        ConnectionMgr.Instance.MessageEvent += OnMessageReceived;
    }

    // 事件處理常式 (在 Worker Thread 執行緒觸發)
    private void OnMessageReceived(string message)
    {
        // 必須透過 Invoke 切換到 UI 執行緒
        if (this.InvokeRequired)
        {
            this.Invoke(new Action(UpdateMessage), message);
        }
        else
        {
            UpdateMessage(message);
        }
    }

    // 實際更新 UI 的方法 (執行於 UI 執行緒)
    private void UpdateMessage(string message)
    {
        eventText.Text = message; // eventText 為 Label 或 TextBox
    }

    // 測試按鈕
    private void btnTestConnect_Click(object sender, EventArgs e)
    {
        ConnectionMgr.Instance.SimulateConnect();
    }

    private void btnTestDisconnect_Click(object sender, EventArgs e)
    {
        ConnectionMgr.Instance.SimulateDisconnect();
    }
}

關鍵技術點解析

技術點說明
InvokeRequired檢查呼叫端是否為 UI 執行緒;若為 true 需使用 Invoke/BeginInvoke
Invoke(delegate, args)同步封送:封鎖呼叫端直到 UI 執行緒處理完成
BeginInvoke非同步封送:立即返回,不阻塞 Worker Thread
Action泛型委派,避免手寫委派型別,支援 Lambda 簡潔語法
事件副本模式var handler = Event; handler?.Invoke(...) 防止多執行緒競態導致 NullReference

進階擴充:泛型事件參數

若需傳遞複雜狀態物件,可定義泛型事件:

public class ConnectionStateEventArgs : EventArgs
{
    public bool IsConnected { get; set; }
    public string NetworkType { get; set; } // WiFi/GPRS/3G
    public int SignalStrength { get; set; }
    public string ErrorMessage { get; set; }
}

// Worker Thread
public event EventHandler StateChanged;

// UI Thread
private void OnStateChanged(object sender, ConnectionStateEventArgs e)
{
    this.Invoke(() => UpdateUI(e));
}

注意事項與最佳實務

  1. 避免在 Worker Thread 建立 UI 物件:所有 UI 控制項必須在 UI 執行緒建立。
  2. Invoke 死鎖風險:若 Worker Thread 呼叫 Invoke 並等待,同時 UI Thread 正等待 Worker Thread (如 Join、WaitOne),會發生死鎖。改用 BeginInvoke 或重構同步邏輯。
  3. 表單關閉時取消訂閱:在 FormClosing 移除事件訂閱,防止記憶體洩漏與存取已釋放控制項。
  4. CF 版本差異:.NET CF 2.0/3.5 均支援 Control.Invoke,行為與完整框架一致。

替代方案對比

方案優點缺點
事件 + Invoke (本文)標準、鬆耦合、易理解需手寫 Invoke 判斷
BackgroundWorker內建 ProgressChanged/RunWorkerCompleted 自動封送CF 版本支援有限、靈活度較低
SynchronizationContext更通用、可在非 WinForms 環境使用CF 實作不完整
訊息泵底層控制、效能極致複雜、易出錯、非必要勿用

結語

事件訂閱 + Control.Invoke 是 .NET Compact Framework 多執行緒 UI 更新的標準模式。掌握 InvokeRequired 判斷、委派封送、事件副本防競態三大要點,即可構建穩健的背景作業與 UI 互動架構。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved