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 是当前推荐的方式,因为它提供了更清晰的异步代码结构。

相关推荐
mocoding11 小时前
使用Flutter设置UI三方库card_settings_ui重构鸿蒙版天气预报我的页面
flutter·ui·harmonyos
初九之潜龙勿用12 小时前
C# 操作Word模拟解析HTML标记之背景色
开发语言·c#·word·.net·office
雨季66612 小时前
Flutter 三端应用实战:OpenHarmony 简易点击计数器与循环颜色反馈器开发指南
开发语言·flutter·ui·ecmascript·dart
时光追逐者12 小时前
使用 MWGA 帮助 7 万行 Winforms 程序快速迁移到 WEB 前端
前端·c#·.net
老骥伏枥~14 小时前
【C# 入门】程序结构与 Main 方法
开发语言·c#
全栈师14 小时前
java和C#的基本语法区别
java·开发语言·c#
钰fly14 小时前
联合编程(加载单个工具,ini读写,图片读写,setting存储)
c#
雨季66616 小时前
Flutter 三端应用实战:OpenHarmony 简易“动态主题切换卡片”交互模式
flutter·ui·交互·dart
FuckPatience16 小时前
C# 对象初始化器对属性赋值vs构造函数里对属性赋值
c#
m0_7482331717 小时前
C语言vsC#:核心差异全解析
c语言·开发语言·c#