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

相关推荐
时光追逐者2 小时前
一款基于 .NET WinForm 开源、轻量且功能强大的节点编辑器,采用纯 GDI+ 绘制无任何依赖库仅仅100+Kb
c#·.net·winform
sali-tec2 小时前
C# 基于halcon的视觉工作流-章58-输出点云图
开发语言·人工智能·算法·计算机视觉·c#
白雪公主的后妈3 小时前
Auto CAD二次开发——文字样式
c#·cad二次开发·文字样式
智者知已应修善业3 小时前
【c# 想一句话把 List<List<string>>的元素合并成List<string>】2023-2-9
经验分享·笔记·算法·c#·list
FuckPatience3 小时前
C# 接口隔离的一个案例
c#
津津有味道5 小时前
Ntag 424 DNA写入URI网址配置开启动态UID计数器镜像C#源码
c#·uri·ndef·424dna·动态uid·计数器镜像
万19998 小时前
asp.net core webapi------3.AutoMapper的使用
c#·.netcore
唐青枫8 小时前
C#.NET 路由机制深入解析:从传统路由到 Endpoint Routing
c#·.net
hixiong12319 小时前
C# OpenCVSharp使用 读光-票证检测矫正模型
人工智能·opencv·c#
霜绛19 小时前
C#知识补充(二)——命名空间、泛型、委托和事件
开发语言·学习·unity·c#