STA 线程模型
[STAThread]
,否则某些功能(如剪贴板)会异常。Application.Run()
启动,处理窗口事件(如点击、重绘)。线程安全与异常
InvalidOperationException: Cross-thread operation not valid
。Control.InvokeRequired
检查是否需要跨线程调用。Control.Invoke/BeginInvoke
csharp
// 同步调用(阻塞当前线程)
control.Invoke(new Action(() => textBox.Text = "Done"));
// 异步调用(立即返回)
control.BeginInvoke(new Action(() => textBox.Text = "Done"));
PostMessage
将委托包装成 Windows 消息,投递到 UI 线程的消息队列。async/await 模式(推荐)
csharp
private async void LoadData()
{
var data = await Task.Run(() => FetchDataFromAPI()); // 后台执行
textBox.Text = data; // 自动回到 UI 线程
}
ConfigureAwait(false)
)。BackgroundWorker
csharp
var worker = new BackgroundWorker { WorkerReportsProgress = true };
worker.DoWork += (s, e) =>
{
for (int i = 0; i < 100; i++)
{
worker.ReportProgress(i);
Thread.Sleep(100);
}
};
worker.ProgressChanged += (s, e) => progressBar.Value = e.ProgressPercentage;
worker.RunWorkerAsync();
SynchronizationContext
csharp
var uiContext = SynchronizationContext.Current;
Task.Run(() =>
{
// 后台操作
uiContext.Post(_ => label.Text = "Complete", null);
});
锁机制
lock
关键字保护临界区。 csharp
private object _locker = new object();
lock (_locker)
{
sharedCounter++;
}
Monitor.Enter/Exit
:更细粒度的控制。Mutex
:跨进程同步。线程安全集合
ConcurrentQueue
、BlockingCollection
。csharp
var logQueue = new ConcurrentQueue();
Task.Run(() =>
{
while (true)
{
if (logQueue.TryDequeue(out string message))
{
BeginInvoke(new Action(() => textBox.AppendText(message)));
}
}
});
高频 UI 更新优化
Invoke
导致性能瓶颈。csharp
private StringBuilder _buffer = new StringBuilder();
private Timer _timer = new Timer { Interval = 100 };
void Initialize()
{
_timer.Tick += (s, e) =>
{
if (_buffer.Length > 0)
{
textBox.AppendText(_buffer.ToString());
_buffer.Clear();
}
};
_timer.Start();
}
// 后台线程调用
void SafeAppendText(string text)
{
lock (_buffer)
{
_buffer.AppendLine(text);
}
}
多窗口与消息循环
csharp
new Thread(() =>
{
Form form = new Form();
Application.Run(form); // 启动消息循环
}).Start();
ShowDialog()
需手动启动循环。死锁预防
csharp
// 错误示例:在 UI 线程调用 Task.Result
var result = Task.Run(() => GetData()).Result; // 导致死锁
async/await
替代阻塞操作。线程追踪
csharp
Debug.WriteLine($"Current Thread: {Thread.CurrentThread.ManagedThreadId}");
性能监控
Process\Thread Count
:监控线程泄漏。.NET CLR Memory\Gen 0 Collections
:检测频繁 GC。Stopwatch
测量主线程耗时。消息循环检测
csharp
if (Application.OpenForms.Count > 0 && !Application.OpenForms[0].IsHandleCreated)
{
Debug.WriteLine("UI 线程未初始化!");
}
场景 | 推荐方案 | 关键注意事项 |
---|---|---|
简单后台任务 | async/await + Task.Run |
避免 async void (除事件处理器外) |
进度报告 | IProgress + Progress |
自动捕获同步上下文 |
资源竞争 | lock + 线程安全集合 |
锁粒度尽量小 |
高频 UI 更新 | 缓冲区 + Timer 合并 | 控制 Timer 触发间隔(50-200ms) |
跨线程创建控件 | 确保 Control.Handle 已创建 |
检查 IsHandleCreated |
第三方库回调 | 在回调中手动调用 Invoke |
避免直接操作 UI |
问题现象 | 原因分析 | 解决方案 |
---|---|---|
UI 冻结无响应 | 主线程执行耗时操作 | 使用 Task.Run 或 BackgroundWorker |
跨线程异常 | 非 UI 线程直接操作控件 | 检查 InvokeRequired 并调用 Invoke |
窗口无法关闭 | 非 UI 线程未启动消息循环 | 在新线程中调用 Application.Run() |
进度条卡顿 | 高频调用 Invoke |
使用缓冲区合并更新 |
随机崩溃(AccessViolation) | 多线程访问释放的控件句柄 | 确保操作前 IsHandleCreated 为 true |
消息泵(Message Pump)
csharp
while (GetMessage(out var msg, IntPtr.Zero, 0, 0))
{
TranslateMessage(ref msg);
DispatchMessage(ref msg);
}
WM_PAINT
(重绘)、WM_CLOSE
(关闭)、WM_USER
(自定义消息)。自定义消息处理
WndProc
: csharp
protected override void WndProc(ref Message m)
{
const int WM_MY_CUSTOM = 0x0400 + 1;
if (m.Msg == WM_MY_CUSTOM)
{
HandleCustomMessage();
return;
}
base.WndProc(ref m);
}
csharp
SendMessage(handle, WM_MY_CUSTOM, IntPtr.Zero, IntPtr.Zero);
处理异步回调
csharp
serialPort.DataReceived += (sender, args) =>
{
if (textBox.InvokeRequired)
{
BeginInvoke(new Action(UpdateTextBox));
}
else
{
UpdateTextBox();
}
};
跨线程初始化控件
csharp
public Form1()
{
InitializeComponent();
// 强制创建控件句柄
var _ = textBox.Handle;
}
核心原则
进阶学习
SendMessage
、PostMessage
。性能调优
SuspendLayout/ResumeLayout
。