聊透多线程编程-线程基础-3.C# Thread 如何从非UI线程直接更新UI元素

news2025/4/18 11:16:43

目录

1. 使用 Control.Invoke 或 Control.BeginInvoke(Windows Forms)

2. 使用 Dispatcher.Invoke 或 Dispatcher.BeginInvoke(WPF)

3. 使用 SynchronizationContext


 

桌面应用程序(如 Windows Forms 或 WPF)中,UI 操作必须由主线程(也称 UI 线程)执行。如果尝试从非 UI 线程直接更新 UI 元素,通常会引发异常或导致不可预测的行为。

Thread 类本身无法直接更新 UI,但可以通过以下方法将操作委托给 UI 线程来实现安全的 UI 更新。


1. 使用 Control.Invoke 或 Control.BeginInvoke(Windows Forms)

在 Windows Forms 应用程序中,可以使用 Control.Invoke 或 Control.BeginInvoke 方法将代码调度到 UI 线程。

示例代码:

using System;
using System.Reflection.Emit;
using System.Threading;
using System.Windows.Forms;
using static System.Net.Mime.MediaTypeNames;

class Program : Form
{
    private Button button;
    private Label label;

    public Program()
    {
        button = new Button { Text = "Start Thread", Dock = DockStyle.Top };
        label = new Label { Text = "Waiting...", Dock = DockStyle.Fill };

        button.Click += Button_Click;

        Controls.Add(label);
        Controls.Add(button);
    }

    private void Button_Click(object sender, EventArgs e)
    {
        Thread thread = new Thread(UpdateLabel);
        thread.Start();
    }

    private void UpdateLabel()
    {
        for (int i = 0; i < 10; i++)
        {
            // 检查是否需要调用 Invoke
            if (label.InvokeRequired)
            {
                // 使用 Invoke 将操作调度到 UI 线程
                label.Invoke(new Action(() => label.Text = $"Count: {i}"));
            }
            else
            {
                // 如果当前线程是 UI 线程,则直接更新
                label.Text = $"Count: {i}";
            }

            Thread.Sleep(500); // 模拟工作
        }
    }

    [STAThread]
    static void Main()
    {
        Application.EnableVisualStyles();
        Application.Run(new Program());
    }
}

解释:

  • InvokeRequired:检查当前线程是否是创建控件的线程(即 UI 线程)。如果不是,则需要通过 Invoke 或 BeginInvoke 调度到 UI 线程。
  • Invoke:同步执行指定的操作,等待操作完成后再继续。
  • BeginInvoke:异步执行指定的操作,不阻塞当前线程。

 

输出效果:
点击按钮后,label 的文本会每 500 毫秒更新一次,显示当前计数值。


2. 使用 Dispatcher.Invoke 或 Dispatcher.BeginInvoke(WPF)

在 WPF 应用程序中,可以使用 Dispatcher 对象将操作调度到 UI 线程。

示例代码:

using System;
using System.Reflection.Metadata;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using static System.Net.Mime.MediaTypeNames;

class MainWindow : Window
{
    private Button button;
    private TextBlock textBlock;

    public MainWindow()
    {
        button = new Button { Content = "Start Thread" };
        textBlock = new TextBlock { Text = "Waiting..." };

        button.Click += Button_Click;

        var stackPanel = new StackPanel();
        stackPanel.Children.Add(button);
        stackPanel.Children.Add(textBlock);

        Content = stackPanel;
    }

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        Thread thread = new Thread(UpdateTextBlock);
        thread.Start();
    }

    private void UpdateTextBlock()
    {
        for (int i = 0; i < 10; i++)
        {
            // 使用 Dispatcher 将操作调度到 UI 线程
            textBlock.Dispatcher.Invoke(() => textBlock.Text = $"Count: {i}");

            Thread.Sleep(500); // 模拟工作
        }
    }
}

class Program
{
    [STAThread]
    static void Main()
    {
        var app = new Application();
        app.Run(new MainWindow());
    }
}

解释:

  • Dispatcher.Invoke:同步执行指定的操作,确保操作在 UI 线程上运行。
  • Dispatcher.BeginInvoke:异步执行指定的操作,不阻塞当前线程。

输出效果:

点击按钮后,TextBlock 的文本会每 500 毫秒更新一次,显示当前计数值。


3. 使用 SynchronizationContext

SynchronizationContext 是一种更通用的方式,适用于 Windows Forms 和 WPF,甚至其他框架(如 ASP.NET)。它允许你捕获当前线程的上下文,并在需要时将其用于调度操作。

示例代码:

using System;
using System.Reflection.Emit;
using System.Threading;
using System.Windows.Forms;
using static System.Net.Mime.MediaTypeNames;

class Program : Form
{
    private Button button;
    private Label label;

    private SynchronizationContext _uiContext;

    public Program()
    {
        button = new Button { Text = "Start Thread", Dock = DockStyle.Top };
        label = new Label { Text = "Waiting...", Dock = DockStyle.Fill };

        button.Click += Button_Click;

        Controls.Add(label);
        Controls.Add(button);

        // 捕获 UI 线程的上下文
        _uiContext = SynchronizationContext.Current;
    }

    private void Button_Click(object sender, EventArgs e)
    {
        Thread thread = new Thread(UpdateLabel);
        thread.Start();
    }

    private void UpdateLabel()
    {
        for (int i = 0; i < 10; i++)
        {
            // 使用 SynchronizationContext 将操作调度到 UI 线程
            _uiContext.Post(_ => label.Text = $"Count: {i}", null);

            Thread.Sleep(500); // 模拟工作
        }
    }

    [STAThread]
    static void Main()
    {
        Application.EnableVisualStyles();
        Application.Run(new Program());
    }
}

解释:

  • SynchronizationContext.Current:捕获当前线程的上下文(通常是 UI 线程的上下文)。
  • Post:异步执行指定的操作。
  • Send:同步执行指定的操作。

输出效果:

点击按钮后,label 的文本会每 500 毫秒更新一次,显示当前计数值。

 

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2331607.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

VMware Fusion Pro 13 for Mac虚拟机

VMware Fusion Pro 13 for Mac虚拟机 文章目录 VMware Fusion Pro 13 for Mac虚拟机一、介绍二、效果下载 一、介绍 VMware Fusion Pro for Mac&#xff0c;是一款mac虚拟机软件&#xff0c;跟Parallels Desktop一样&#xff0c;都可以让你的 Mac 同时运行一个或多个不同的操作…

7.第二阶段x64游戏实战-string类

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 本次游戏没法给 内容参考于&#xff1a;微尘网络安全 上一个内容&#xff1a;7.第二阶段x64游戏实战-分析人物属性 string类是字符串类&#xff0c;在计算机中…

【debug莫名其妙跑飞了】

现象&#xff1a;就是在初始化汇编里跑飞了&#xff0c;也可能运行起来时钟不对 原因&#xff1a;调试器调试程序时会执行reset复位&#xff0c;reset没有正确执行。 细节决定成败&#xff0c;事出反常必有妖&#xff0c;忽略的小卡拉米最后能玩死你啊

基础知识补充篇:什么是DAPP前端连接中的provider

专栏:区块链入门到放弃查看目录-CSDN博客文章浏览阅读352次。为了方便查看将本专栏的所有内容列出目录,按照顺序查看即可。后续也会在此规划一下后续内容,因此如果遇到不能点击的,代表还没有更新。声明:文中所出观点大多数源于笔者多年开发经验所总结,如果你想要知道区块…

openssl源码分析之加密模式(modes)

openssl实现分组加密模式&#xff08;例如AES128-CBC的CBC部分&#xff09;的模块名字叫做modes&#xff0c;源代码位于 https://gitee.com/gh_mirrors/openssl/tree/master/crypto/modes 博主又打不开github了TT&#xff0c;只能找个gitee镜像 头文件是modes.h。 该模块目前…

【PVR】《Palm Vein Recognition and Large-scale Research based on Deep Learning》

邬晓毅. 基于深度学习的掌静脉识别及规模化研究[D]. 四川:电子科技大学,2024. 文章目录 1、背景2、相关工作3、创新点和贡献4、方法和实验4.1、知识介绍4.2、基于自适应损失函数的掌静脉识别算法研究4.3、退化图像的掌静脉识别鲁棒性提升研究4.4、掌静脉识别系统规模化 5、总结…

PyQt学习记录

PyQt学习记录 要在界面上 创建一个控件&#xff0c;就需要在程序代码中 创建 这个 控件对应类 地一个 实例对象。 在Qt系统中&#xff0c;控件&#xff08;widget&#xff09;是 层层嵌套 的&#xff0c;除了最顶层的控件&#xff0c;其他的控件都有父控件。 几个函数 函数mo…

Linux 学习笔记(5)路径知识详解:绝对路径、相对路径与特殊路径符(期末、期中复习必备)

前言 一、相对路径与绝对路径 1、概念阐述 2、实际示例 二、特殊路径符 1.特殊路径符介绍 2.应用场景 三、总结 四、结语 前言 在 Linux 系统的学习过程中&#xff0c;路径的概念至关重要&#xff0c;它是我们在文件系统中定位文件和目录的关键。今天&#xff0c;我们就…

Trae + LangGPT 生成结构化 Prompt

Trae LangGPT 生成结构化 Prompt 0. 引言1. 安装 Trae2. 克隆 LangGPT3. Trae 和 LangGPT 联动4. 集成到 Dify 中 0. 引言 Github 上 LangGPT 这个项目&#xff0c;主要向我们介绍了写结构化Prompt的一些方法和示例&#xff0c;我们怎么直接使用这个项目&#xff0c;辅助我们…

动态规划——两个数组的dp问题

目录 1. 最长公共子序列 2. 不相交的线 3. 不同的子序列 4. 通配符匹配 5. 正则表达式匹配 6. 交错字符串 7. 两个字符串的最小ASCII删除和 8. 最长重复子数组 1. 最长公共子序列 题目链接&#xff1a;1143. 最长公共子序列 - 力扣&#xff08;LeetCode&#xff0…

stream流Collectors.toMap(),key值重复问题

文章目录 一、问题二、问题示例三、原因四、解决方法4.1、方案一 一、问题 发现Collectors.toMap的一个坑&#xff0c;若key值重复的时候会抛异常。如&#xff1a; IllegalStateException: Duplicate key 男 二、问题示例 报错示例如下&#xff1a; import lombok.AllArgsC…

机器学习 Day10 逻辑回归

1.简介 流程就是&#xff1a; 就是我们希望回归后激活函数给出的概率越是1和0. 2.API介绍 sklearn.linear_model.LogisticRegression 是 scikit-learn 库中用于实现逻辑回归算法的类&#xff0c;主要用于二分类或多分类问题。以下是对其重要参数的详细介绍&#xff1a; 2.1.…

Seq2Seq - Dataset 类

本节代码定义了一个 CMN 类&#xff0c;它继承自 PyTorch 的 Dataset 类&#xff0c;用于处理英文和中文的平行语料库。这个类的主要作用是将文本数据转换为模型可以处理的格式&#xff0c;并进行必要的填充操作&#xff0c;以确保所有序列的长度一致。 ⭐重写Dataset类是模型训…

echarts图表相关

echarts图表相关 echarts官网折线图实际开发场景一&#xff1a; echarts官网 echarts官网 折线图 实际开发场景一&#xff1a; 只有一条折线&#xff0c;一半实线&#xff0c;一半虚线。 option {tooltip: {trigger: "axis",formatter: (params: any) > {const …

idea自动部署jar包到服务器Alibaba Cloud Toolkit

安装插件&#xff1a;Alibaba Cloud Toolkit 配置服务器: 服务器配置&#xff1a; 项目启动Shell脚本命令: projectpd-otb.jar echo 根据项目名称查询对应的pid pid$(pgrep -f $project); echo $pid echo 杀掉对应的进程&#xff0c;如果pid不存在&#xff0c;则不执行 if [ …

Element Plus 图标使用方式整理

Element Plus 图标使用方式整理 以下是 Element Plus 图标的所有使用方式&#xff0c;包含完整代码示例和总结表格&#xff1a; 1. 按需引入图标组件 适用场景&#xff1a;仅需少量图标时&#xff0c;按需导入减少打包体积 示例代码&#xff1a; <template><div>…

链路聚合+vrrp

1.链路聚合 作用注意事项将多个物理接口&#xff08;线路&#xff09;逻辑上绑定在一起形成一条逻辑链路&#xff0c;起到叠加带宽的作用1.聚合接口必须转发速率一致。2.聚合设备两端必须一致 配置命令 方法一 [Huawei]interface Eth-Trunk 0----先创建聚合接口&#xff0c;…

Dynamics 365 Business Central Register Customer Payment 客户付款登记

#Dynamics 365 BC ERP# #D365 ERP# #Navision 前言 在实施过程&#xff0c;经常给客户介绍的 给客户付款一般用Payment Journal. 在客户熟悉系统运行后&#xff0c;往往会推荐客户使用Register Customer Payment.用这个function 工作会快很多&#xff0c;但出错的机会也比较大…

Odoo免费开源ERP:企业销售过程中出现的问题

在企业未上线Odoo免费开源ERP时&#xff0c;企业销售过程中会存在失误。比如&#xff0c;许多销售订单都有如下问题&#xff1a;不当的定价、向客户过多地询问、处理订单延误、错过发货日期等。这些问题源于企业三个未集成的信息系统&#xff1a;销售管理系统、库存系统和财务系…

网络稳定性--LCA+最大生成树+bfs1/dfs1找最小边

1.最大生成树去除重边&#xff0c;只要最大的边成树 2.LCA查最近公共祖先&#xff0c;然后询问的lca(x,y)ff,分别从x,y向上找最小边 3.bfs1/dfs1就是2.中向上找的具体实现 #include<bits/stdc.h> using namespace std; #define N 100011 typedef long long ll; typede…