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

相关推荐
唐青枫7 小时前
C#.NET dapper 详解
c#·.net
死也不注释10 小时前
【鸡零狗碎记录】
unity·c#
Maybe_ch10 小时前
.NET-键控服务依赖注入
开发语言·c#·.net
liulilittle13 小时前
C++/CLI与标准C++的语法差异(一)
开发语言·c++·.net·cli·clr·托管·原生
白玉cfc13 小时前
【iOS】网易云仿写
ui·ios·objective-c
★YUI★13 小时前
学习游戏制作记录(剑投掷技能)7.26
学习·游戏·unity·c#
小乖兽技术14 小时前
C#与C++交互开发系列(二十四):WinForms 应用中嵌入C++ 原生窗体
c++·c#·交互
界面开发小八哥15 小时前
界面组件DevExpress WPF中文教程:Grid - 如何过滤节点?
.net·wpf·界面控件·devexpress·ui开发
I'mSQL15 小时前
C#与WPF使用mvvm简单案例点击按钮触发弹窗
开发语言·c#·wpf
工藤新一OL15 小时前
把xml的格式从utf-8-bom转为utf-8
xml·c#·asp.net·.netcore·visual studio