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

相关推荐
CodeCraft Studio11 分钟前
PDF处理控件Aspose.PDF教程:在C#中将PDF转换为Base64
服务器·pdf·c#·.net·aspose·aspose.pdf·pdf转base64
咕白m6253 小时前
C# 将多张图片转换到一个 PDF 文档
c#·.net
渡我白衣3 小时前
未来的 AI 操作系统(三)——智能的中枢:从模型到系统的统一
人工智能·深度学习·ui·语言模型·人机交互
张人玉3 小时前
c#WPF基础知识
开发语言·c#·wpf
YuanlongWang5 小时前
C# 基础——值类型与引用类型的本质区别
java·jvm·c#
唐青枫7 小时前
C#.NET FluentValidation 全面解析:优雅实现对象验证
c#·.net
Aevget13 小时前
DevExpress WPF中文教程:Data Grid - 如何使用虚拟源?(二)
.net·wpf·界面控件·devexpress·ui开发·数据网格
从孑开始15 小时前
ManySpeech.MoonshineAsr 使用指南
人工智能·ai·c#·.net·私有化部署·语音识别·onnx·asr·moonshine
YuanlongWang16 小时前
C# 中,依赖注入(DI)的实现方式
c#
Element_南笙17 小时前
吴恩达新课程:Agentic AI(笔记2)
数据库·人工智能·笔记·python·深度学习·ui·自然语言处理