VB.NET,C#在线程中修改UI的安全操作

vbnet 复制代码
    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 应用 需要精确控制线程调度的场景 异步编程场景

最佳实践

  1. 优先使用 async/await:语法最简洁,适合现代 C# 开发。
  2. 避免频繁 Invoke:如果需要更新大量数据,考虑批量处理后再更新 UI,减少 UI 线程负担。
  3. 使用弱引用:在长时间运行的后台任务中,避免直接引用 UI 控件,防止内存泄漏。
  4. 异常处理:确保在 UI 更新代码中包含适当的异常处理,避免因异常导致 UI 线程崩溃。

根据您的具体场景选择合适的方法,async/await 是当前推荐的方式,因为它提供了更清晰的异步代码结构。

相关推荐
sg_knight18 小时前
设计模式实战:状态模式(State)
python·ui·设计模式·状态模式·state
周杰伦fans19 小时前
C# required 关键字详解
开发语言·网络·c#
黄思搏19 小时前
基于标注平台数据的 Unity UI 自动化构建工作流设计与工程实践
ui·unity·蓝湖·vectoui
小樱花的樱花21 小时前
1 项目概述
开发语言·c++·qt·ui
2301_8227032021 小时前
开源鸿蒙跨平台Flutter开发:跨端图形渲染引擎的类型边界与命名空间陷阱:以多维雷达图绘制中的 dart:ui 及 StrokeJoin 异常为例
算法·flutter·ui·开源·图形渲染·harmonyos·鸿蒙
游乐码21 小时前
c#ArrayList
开发语言·c#
唐青枫1 天前
C#.NET Monitor 与 Mutex 深入解析:进程内同步、跨进程互斥与使用边界
c#·.net
周杰伦fans1 天前
cad文件选项卡不见了怎么办?
c#
llm大模型算法工程师weng1 天前
Python敏感词检测方案详解
开发语言·python·c#
会写代码的建筑师1 天前
.NET 控制台后台程序实践细节总结
后端·.net