主視窗執行緒建立子執行緒監聽 Socket,子執行緒收到資料後引發事件,主視窗處理事件時建立新視窗 (new Form().Show())。結果視窗出現但「反白、無回應、標題列顯示未回應」,像死當一樣。
Form.Show() 建立視窗,但該視窗沒有訊息迴圈 (Application.Run),無法處理 WM_PAINT 等訊息,導致畫面不重繪、無法互動。將建立視窗的代碼封裝在委派中,透過 Invoke 切換到 UI 執行緒執行。
// 子執行緒中
private void OnDataReceived(object sender, DataEventArgs e)
{
var frm = new ChildForm(); // 在子執行緒建立
frm.Show(); // 視窗無訊息泵,掛起
}
// 定義委派
private delegate void ShowChildFormDelegate();
private void OnDataReceived(object sender, DataEventArgs e)
{
// 檢查是否需要跨執行緒
if (this.InvokeRequired)
{
// 切換到 UI 執行緒同步執行
this.Invoke(new ShowChildFormDelegate(ShowChildForm));
}
else
{
ShowChildForm();
}
}
private void ShowChildForm()
{
var frm = new ChildForm();
frm.Show(); // 現在在 UI 執行緒,正常運作
}
| 方法 | 行為 | 適用場景 |
|---|---|---|
Invoke | 同步,阻塞呼叫執行緒直到 UI 執行緒完成 | 需等待 UI 更新完成再繼續 |
BeginInvoke | 非同步,立即返回,UI 執行緒稍後執行 | 不需等待、避免死結、高頻率更新 |
using System;
using System.Threading;
using System.Runtime.Remoting.Messaging;
public delegate bool FactorizeDelegate(int n, ref int f1, ref int f2);
public class PrimeFactorizer
{
public bool Factorize(int n, ref int f1, ref int f2)
{
f1 = 1; f2 = n;
for (int i = 2; i < n; i++)
if (n % i == 0) { f1 = i; f2 = n / i; break; }
return f1 != 1;
}
}
public class ResultHandler
{
private int _number;
public ResultHandler(int n) => _number = n;
[System.Runtime.Remoting.Messaging.OneWay]
public void OnCompleted(IAsyncResult ar)
{
var del = (FactorizeDelegate)((AsyncResult)ar).AsyncDelegate;
del.EndInvoke(ref f1, ref f2, ar);
Console.WriteLine($"[{_number}] 因子: {f1} x {f2}");
}
private int f1, f2;
}
public class Program
{
public static void Main()
{
var factorizer = new PrimeFactorizer();
var del = new FactorizeDelegate(factorizer.Factorize);
var handler = new ResultHandler(1000589023);
// 非同步呼叫 + 回呼
del.BeginInvoke(1000589023, ref _, ref _, handler.OnCompleted, null);
Console.WriteLine("主執行緒繼續做其他事...");
Thread.Sleep(2000); // 等待示範
}
private static int _;
}
// UI 事件處理器
private async void btnStart_Click(object sender, EventArgs e)
{
btnStart.Enabled = false;
try
{
// 背景執行緒運算
var result = await Task.Run(() => HeavyCompute());
// 自動切回 UI 執行緒
txtResult.Text = result;
}
finally { btnStart.Enabled = true; }
}
InvokeRequired 檢查。Task.Run,await 後自動回 UI 執行緒。Load 事件中做耗時工作,改用 Shown 或背景載入。-=) 避免記憶體洩漏。SynchronizationContext 或 TaskScheduler.FromCurrentSynchronizationContext() 進行進階排程。