出现“线程无法访问非本线程创建的资源”的错误

news2024/9/20 14:32:51

出现原因

在WinForm中,如果你尝试在一个线程上操作另一个线程创建的控件,就会出现“线程无法访问非本线程创建的资源”的错误。这是因为Windows窗体的设计原则是单线程模型,即只有创建该控件的线程才能对其进行操作。

解决方法 

1.使用 Control.InvokeControl.BeginInvoke 方法

这是处理跨线程操作的最常见和推荐的方法。当你在一个线程中创建了一个控件,然后另一个线程想要访问这个控件时,你可以使用 Control.InvokeControl.BeginInvoke 方法。

Control.Invoke 方法会阻塞当前线程,直到操作完成。而 Control.BeginInvoke 方法则是异步的,它会立即返回,并在后台执行操作。

这两个方法都需要一个委托作为参数,这个委托封装了你想要执行的操作。这些操作会在创建控件的线程上执行,因此是线程安全的。

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        Thread thread1 = new Thread(() =>
        {
            for (int i = 1; i <= 100; i += 2)
            {
                UpdateTextBox(textBox1, i.ToString());
                Thread.Sleep(100);
            }
        });

        Thread thread2 = new Thread(() =>
        {
            for (int i = 2; i <= 100; i += 2)
            {
                UpdateTextBox(textBox2, i.ToString());
                Thread.Sleep(100);
            }
        });

        thread1.Start();
        thread2.Start();
    }

    private void UpdateTextBox(TextBox textBox, string text)
    {
        if (textBox.InvokeRequired)
        {
            textBox.Invoke((MethodInvoker)delegate { UpdateTextBox(textBox, text); });
        }
        else
        {
            textBox.Text = text;
        }
    }
}

Control.InvokeRequired属性用于检查当前线程是否是创建控件的线程。如果当前线程是创建控件的线程,InvokeRequired返回false;如果当前线程不是创建控件的线程,InvokeRequired返回true

因此,我们在访问控件之前先检查InvokeRequired属性,如果它返回true,那么我们就使用Control.Invoke方法来在创建控件的线程上执行操作。Control.Invoke方法接收一个委托,并在创建控件的线程上同步执行该委托。

这样做的目的是确保我们总是在正确的线程上访问控件,避免线程冲突,保证应用程序的稳定性和正确性。

在这个方法中,如果InvokeRequired返回true,我们就使用Control.Invoke方法来重新调用UpdateTextBox方法。这次调用将在创建textBox的线程上执行,所以InvokeRequired将返回false,然后我们就可以安全地更新textBox的Text属性。

2.使用 BackgroundWorker 组件

BackgroundWorker 组件提供了对后台操作的支持。你可以在 DoWork 事件处理程序中执行后台操作,然后在 ProgressChangedRunWorkerCompleted 事件处理程序中更新 UI 控件。

因为这些事件处理程序是在创建 BackgroundWorker 的线程上引发的,所以你可以在这些事件处理程序中安全地访问 UI 控件。

BackgroundWorker 组件还支持报告进度和取消操作,这使得它非常适合用于长时间运行的操作。

using System;
using System.ComponentModel;
using System.Threading;
using System.Windows.Forms;

namespace MultiThreadedForm
{
    public partial class Form1 : Form
    {
        private BackgroundWorker worker1;
        private BackgroundWorker worker2;

        public Form1()
        {
            InitializeComponent();
            InitializeBackgroundWorkers();
        }

        private void InitializeBackgroundWorkers()
        {
            // 初始化worker1
            worker1 = new BackgroundWorker();
            worker1.WorkerReportsProgress = true;
            worker1.DoWork += new DoWorkEventHandler(Worker1_DoWork);
            worker1.ProgressChanged += new ProgressChangedEventHandler(Worker1_ProgressChanged);
            worker1.RunWorkerCompleted += new RunWorkerCompletedEventHandler(Worker1_RunWorkerCompleted);

            // 初始化worker2
            worker2 = new BackgroundWorker();
            worker2.WorkerReportsProgress = true;
            worker2.DoWork += new DoWorkEventHandler(Worker2_DoWork);
            worker2.ProgressChanged += new ProgressChangedEventHandler(Worker2_ProgressChanged);
            worker2.RunWorkerCompleted += new RunWorkerCompletedEventHandler(Worker2_RunWorkerCompleted);
        }

        // 线程1的后台操作
        private void Worker1_DoWork(object sender, DoWorkEventArgs e)
        {
            for (int i = 1; i <= 100; i += 2)
            {
                worker1.ReportProgress(i);
                Thread.Sleep(100);  // 模拟耗时操作
            }
        }

        // 更新textbox1的进度
        private void Worker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            textBox1.Text = e.ProgressPercentage.ToString();
        }

        // 线程1完成后的操作
        private void Worker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            textBox1.Text = "Thread 1 completed!";
        }

        // 线程2的后台操作
        private void Worker2_DoWork(object sender, DoWorkEventArgs e)
        {
            for (int i = 2; i <= 100; i += 2)
            {
                worker2.ReportProgress(i);
                Thread.Sleep(100);  // 模拟耗时操作
            }
        }

        // 更新textbox2的进度
        private void Worker2_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            textBox2.Text = e.ProgressPercentage.ToString();
        }

        // 线程2完成后的操作
        private void Worker2_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            textBox2.Text = "Thread 2 completed!";
        }

        // 开始线程1
        private void StartThread1Button_Click(object sender, EventArgs e)
        {
            if (!worker1.IsBusy)
            {
                worker1.RunWorkerAsync();
            }
        }

        // 开始线程2
        private void StartThread2Button_Click(object sender, EventArgs e)
        {
            if (!worker2.IsBusy)
            {
                worker2.RunWorkerAsync();
            }
        }
    }
}

我们创建了两个BackgroundWorker实例worker1worker2,分别用于控制线程1和线程2的操作。当点击“开始线程1”按钮时,会启动线程1的后台操作,而点击“开始线程2”按钮时,则会启动线程2的后台操作。

DoWork事件处理程序中,分别使用ReportProgress方法报告进度,并在ProgressChanged事件处理程序中对相应的TextBox控件进行更新。在RunWorkerCompleted事件处理程序中,对TextBox控件进行最终的更新。

3.设置 Control.CheckForIllegalCrossThreadCalls 属性

Control.CheckForIllegalCrossThreadCalls 是一个全局设置,会影响到所有的控件。如果你将这个属性设置为 false,那么就可以在任何线程上操作控件,而不会抛出异常。

然而,这种方法并不推荐使用。这是因为它违反了 Windows 窗体的设计原则,可能会导致未定义的行为或者难以调试的问题。除非你完全理解这种方法的风险,并且愿意承担这些风险,否则应该避免使用这种方法。

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

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

相关文章

阿里云双11优惠云服务器99元一年,4年396元

阿里云99元服务器新老用户均可以买&#xff0c;你没看错&#xff0c;老用户可以买&#xff0c;活动页面 aliyunfuwuqi.com/go/aliyun 配置为云服务器ECS经济型e实例、2核2G、3M固定带宽、40G ESSD Entry云盘&#xff0c;并且续费不涨价&#xff0c;原价99元即可续费&#xff0c…

【MySQL进阶之路丨第十六篇】一文带你精通MySQL函数

引言 在上一篇中我们介绍了MySQL数据的导入与导出&#xff1b;在开发中&#xff0c;对MySQL函数的运用是十分重要的。这一篇我们使用命令行方式来帮助读者掌握MySQL中函数的操作。 上一篇链接&#xff1a;【MySQL进阶之路丨第十五篇】一文带你精通MySQL数据的导入与导出 MySQ…

SQL第五次上机实验

1.向图书表&#xff08;Book&#xff09;插入以下记录 USE TSGL GO INSERT INTO Book VALUES(7-5402-1800-3,文学类,边城,沈从文,燕山出版社,10,5,5)2.向借阅表插入以下两条记录 USE TSGL GO INSERT INTO Lend VALUES(201207034201,7-5402-1800-3,00366240,2013-04-22),(2012…

网络运维Day06-补充

文章目录 RAID磁盘阵列RAID0条带模式RAID1镜像模式RAID5高性价比模式RAID01RAID10 逻辑卷一块磁盘的使用流程逻辑卷的使用流程 制作逻辑卷步骤一&#xff1a;添加硬盘步骤二&#xff1a;分区规划步骤三&#xff1a;制作物理卷步骤四&#xff1a;制作卷组步骤五&#xff1a;制作…

【网络知识必知必会】构造HTTP请求的几种方法

文章目录 前言1. 通过 form 表单构造 HTTP 请求1.1 HTML 编程1.2 认识下 HTML1.3 form 发送 GET 请求form 的重要参数:input 的重要参数:使用 Fiddler 查看我们构造的 HTTP 请求体会 form 代码和 HTTP 请求之间的对应关系 1.4 form 发送 POST 请求使用 Fiddler 查看我们构造的 …

【PHP函数封装】分分钟帮你实现数据脱敏处理, 支持手机号码、邮箱、身份证号 中文字符串!

&#x1f680; 个人主页 极客小俊 ✍&#x1f3fb; 作者简介&#xff1a;web开发者、设计师、技术分享博主 &#x1f40b; 希望大家多多支持一下, 我们一起进步&#xff01;&#x1f604; &#x1f3c5; 如果文章对你有帮助的话&#xff0c;欢迎评论 &#x1f4ac;点赞&#x1…

Linux学习之进程三

目录 进程控制 fork函数 什么是写时拷贝 进程终止 mian函数的返回值 退出码 错误码 exit() 进程等待 1.什么是进程等待&#xff1f; 2.为什么要进行进程等待&#xff1f; 3.如何进程进程等待&#xff1f; wait&#xff0c;waitpid&#xff1a; waitpid 进程替换 …

【Git】Gui图形化管理、SSH协议私库集成IDEA使用

一、Gui图形化界面使用 1、根据自己需求打开管理器 2、克隆现有的库 3、图形化界面介绍 1、首先在本地仓库更新一个代码文件&#xff0c;进行使用&#xff1a; 2、进入图形管理界面刷新代码资源&#xff1a; 3、点击Stage changed 跟踪文件&#xff0c;将文件处于暂存区 4、通过…

基于JavaWeb+SpringBoot+Vue摩托车商城微信小程序系统的设计和实现

基于JavaWebSpringBootVue摩托车商城微信小程序系统的设计和实现 源码传送入口前言主要技术系统设计功能截图Lun文目录订阅经典源码专栏Java项目精品实战案例《500套》 源码获取 源码传送入口 前言 近年来&#xff0c;随着移动互联网的快速发展&#xff0c;电子商务越来越受到…

javascript用localStorage存储用户搜索词记录,并在搜索框下展显搜索词记录

//首先是storage的一封装 //storage.js文件 function storage(){//设置storage密钥this.ms"mystorage";}//以下为函数的原型方法//获得localStorage值storage.prototype.getLocalfunction(key){//先检查设置的localStorage的密钥var mydatalocalStorage.getItem(thi…

问题描述:64位计算机的寻址能力是多少TB

问题描述&#xff1a;64位计算机的寻址能力是多少TB 我在看到一个32位电脑的寻址能力计算时&#xff0c;看到是这么计算的。 虚拟内存的大小受到计算机地址位数的限制&#xff0c; 那么32位电脑的寻址能力计算应该是这样 为什么网上百度到的是16TB呢&#xff0c;如下图所示 中…

数据库安全:Hadoop 未授权访问-命令执行漏洞.

数据库安全&#xff1a;Hadoop 未授权访问-命令执行漏洞. Hadoop 未授权访问主要是因为 Hadoop YARN 资源管理系统配置不当&#xff0c;导致可以未经授权进行访问&#xff0c;从而被攻击者恶意利用。攻击者无需认证即可通过 RESTAPI 部署任务来执行任意指令&#xff0c;最终完…

winform打包默认安装路径设置

点击安装程序的 Application Folder 修改属性中的 DefaultLocation

高校教务系统登录页面JS分析——长沙理工大学教务系统

高校教务系统密码加密逻辑及JS逆向 本文将介绍高校教务系统的密码加密逻辑以及使用JavaScript进行逆向分析的过程。通过本文&#xff0c;你将了解到密码加密的基本概念、常用加密算法以及如何通过逆向分析来破解密码。 本文将是本专栏最后一篇文章&#xff0c;我看了绝大多数高…

【编程语言发展史】Go语言的发展历史

目录 Go的起源 Go语言发展时间轴 logo Go的起源 Go 语言起源 2007 年&#xff0c;并于 2009 年正式对外发布。它从 2009 年 9 月 21 日开始作为谷歌公司 20% 兼职项目&#xff0c;即相关员工利用 20% 的空余时间来参与 Go 语言的研发工作。该项目的三位领导者均是著名的 …

1. 深度学习——激活函数

机器学习面试题汇总与解析——激活函数 本章讲解知识点 什么是激活函数&#xff1f; 为什么要使用激活函数&#xff1f; 详细讲解激活函数 本专栏适合于Python已经入门的学生或人士&#xff0c;有一定的编程基础。本专栏适合于算法工程师、机器学习、图像处理求职的学生或人…

CAN轴【禾川】

禾川CAN轴有问题。 厂家说是只能使用禾川的伺服X2EN&#xff0c;和X3EN 添加CAN主站&#xff1a; 网络&#xff1a; 0 波特率&#xff1a; 1000K 添加CAN总线&#xff1a; 主站&#xff1a; 2 同步帧&#xff1a; 80h 设置刷新时间 时间帧&#xff1a;100h 添加伺服&…

野火i.MX6ULL开发板wifi连接、SHH登录玄学篇

1、WiFi连接成功 服了&#xff0c;一样的步骤&#xff0c;它又行了。 手机开热点&#xff0c;2.4G频段&#xff0c;wanghaha&#xff0c;连上显示了IP地址&#xff0c;输入ping 百度网址 等了七八秒它访问成功。 中间还用过usb线刷镜像Debian。 2、使用 MobaXterm SSH 登录…

页表和cache

页表基本原理 页表主要用来将虚拟地址映射到物理地址&#xff0c;在使用虚拟地址访问内存时&#xff0c;微处理器首先将虚拟地址拆分成页号和页内偏移量&#xff0c;然后使用页号在页表中查找对应的物理页框号&#xff0c;将物理页地址加上页内偏移量&#xff0c;得到最终的物…

skynet学习笔记02— skynet介绍、skynet基础API与环境变量

01、Skynet与Actor模型 在系统Skynet之前&#xff0c;先了解一下Skynet与Actor模型&#xff0c;下列是风云大佬的介绍以及一个大佬的博客 https://github.com/cloudwu/skynet/wiki/GettingStartedhttps://blog.csdn.net/qq769651718/article/details/79432793 02、Skynet基础…