Private Delegate Sub UpdateTextDelegate(text As String)
Private Sub ThreadShowNewMessage(text As String)
' 检查当前线程是否是UI线程
If Me.InvokeRequired Then
' 如果不是UI线程,创建委托实例并Invoke
Dim d As New UpdateTextDelegate(AddressOf ThreadShowNewMessage)
Me.Invoke(d, New Object() {text})
Else
' 如果是UI线程,直接更新控件
'Me.ListBox2.Items.Add(text)
Me.ListBox2.Items.Insert(0, text)
End If
End Sub
在 C# 中实现多线程更新 UI 的方法与VB.NET类似,但语法有所不同。以下是几种常见的实现方式:
方法 1:使用 Control.Invoke/BeginInvoke(经典方式)
private delegate void UpdateTextDelegate(string text);
private void ThreadShowNewMessage(string text)
{
// 检查当前线程是否是UI线程
if (this.InvokeRequired)
{
// 如果不是UI线程,创建委托实例并Invoke
UpdateTextDelegate d = new UpdateTextDelegate(ThreadShowNewMessage);
this.Invoke(d, new object[] { text });
}
else
{
// 如果是UI线程,直接更新控件
// this.listBox2.Items.Add(text);
this.listBox2.Items.Insert(0, text);
}
}
方法 2:使用 Lambda 表达式简化 Invoke
private void ThreadShowNewMessage(string text)
{
if (this.listBox2.InvokeRequired)
{
this.listBox2.Invoke(new Action(() => {
listBox2.Items.Insert(0, text);
}));
}
else
{
listBox2.Items.Insert(0, text);
}
}
方法 3:使用 TaskScheduler.FromCurrentSynchronizationContext(推荐)
private TaskScheduler _uiScheduler;
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
_uiScheduler = TaskScheduler.FromCurrentSynchronizationContext(); // 保存UI线程的调度器
}
private void ThreadShowNewMessage(string text)
{
// 使用Task在UI线程上执行
Task.Factory.StartNew(() => {
listBox2.Items.Insert(0, text);
}, CancellationToken.None, TaskCreationOptions.None, _uiScheduler);
}
方法 4:使用 async/await(最简洁)
csharp
private async void ThreadShowNewMessage(string text)
{
if (!this.listBox2.InvokeRequired)
{
listBox2.Items.Insert(0, text);
return;
}
// 等待在UI线程上执行
await Task.Run(() => {
this.listBox2.Invoke(new Action(() => {
listBox2.Items.Insert(0, text);
}));
});
}
方法 5:使用 Dispatcher(WPF 应用)
如果是 WPF 应用,需要使用 Dispatcher
替代 Control.Invoke
:
private void ThreadShowNewMessage(string text)
{
if (this.Dispatcher.CheckAccess()) // 检查是否在UI线程
{
listBox2.Items.Insert(0, text);
}
else
{
this.Dispatcher.Invoke(() => {
listBox2.Items.Insert(0, text);
});
}
}
调用示例 在工作线程中调用上述方法:
// 示例1:在新线程中调用
new Thread(() => {
ThreadShowNewMessage("来自工作线程的消息");
}).Start();
// 示例2:使用Task
Task.Run(() => {
ThreadShowNewMessage("来自Task的消息");
});
关键区别总结
特性 | Control.Invoke | TaskScheduler | async/await |
---|---|---|---|
语法复杂度 | 中等 | 较低 | 最低 |
线程安全性 | 高 | 高 | 高 |
异常处理 | 需要在 Invoke 内部捕获 | 可通过 Task 处理异常 | 可使用 try/catch 直接处理 |
适用场景 | 所有.NET Framework 应用 | 需要精确控制线程调度的场景 | 异步编程场景 |
最佳实践
- 优先使用 async/await:语法最简洁,适合现代 C# 开发。
- 避免频繁 Invoke:如果需要更新大量数据,考虑批量处理后再更新 UI,减少 UI 线程负担。
- 使用弱引用:在长时间运行的后台任务中,避免直接引用 UI 控件,防止内存泄漏。
- 异常处理:确保在 UI 更新代码中包含适当的异常处理,避免因异常导致 UI 线程崩溃。
根据您的具体场景选择合适的方法,async/await 是当前推荐的方式,因为它提供了更清晰的异步代码结构。