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

出现原因

在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 窗体的设计原则,可能会导致未定义的行为或者难以调试的问题。除非你完全理解这种方法的风险,并且愿意承担这些风险,否则应该避免使用这种方法。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【星海随笔】SDN neutron (一)

一、SDN的原理&#xff1a; 控制平面与数据平面分离&#xff1a;传统网络中&#xff0c;网络设备同时承担控制和数据转发功能&#xff0c;而SDN将这两个功能分离&#xff0c;使得网络控制集中在一个中心控制器上。 中心控制器&#xff1a;SDN架构中的中心控制器负责网络的全局…

强化学习 - 策略梯度(Policy Gradient)

引言 强化学习常见的方法为基于值函数或者基于策略梯度。 值函数&#xff1a;值函数最优时得到最优策略&#xff0c;即状态s下&#xff0c;最大行为值函数maxQ(s,a)对应的动作。 但对于机器人连续动作空间&#xff0c;动作连续时&#xff0c;基于值函数&#xff0c;存在以下问…

C语言C位出道心法(五):内存管理

C语言C位出道心法(一):基础语法 C语言C位出道心法(二):结构体|结构体指针|链表 C语言C位出道心法(三):共用体|枚举 C语言C位出道心法(四):文件操作 C语言C位出道心法(五):内存管理 一:C语言内存管理认知 二:C语言中内存堆|栈认知 三:C语言中引用内存丢失认知

秋招进入尾声了,还有哪些公司和岗位可以投递?

24届秋招基本已经进入尾声了&#xff0c;接下来就是秋招补录了&#xff0c;最近在微信群看到一些同学再问哪些公司还在招人的。 在这里跟大家分享一份2024届秋招信息汇总表&#xff0c;目前已更新2000家&#xff0c;不仅有互联网公司&#xff0c;还有外企、国企、各类研究所&am…

使用WinDbg分析CPU100%的问题

在我们软件运行的时候&#xff0c;偶尔会出现CPU占比100%的问题&#xff0c;而且极其不容易排查&#xff0c;概率极低&#xff0c;我硬是操作了一个下午&#xff0c;出现了一次&#xff0c;然后找到了dmp文件&#xff0c;也没有任何的规律&#xff0c;那么就可以借助windbg进行…

轻松下载网页音频和视频

在网页上看到好看的视频或者听到的音乐想保存&#xff0c;让我来教你&#xff08;仅供学习&#xff09; 注意&#xff1a;有极少部分的网站视频经过加密&#xff0c;无法下载 一、视频下载 1.打开视频网页 2.右键“检查” 3.刷新网页 4.按照下图中步骤操作 5.把复制的链接放…

如何利用产品帮助中心提升用户体验

在当今竞争激烈的市场中&#xff0c;提供优秀的用户体验是吸引和保留客户的关键。而一个高效和易于使用的产品帮助中心&#xff0c;正成为越来越多企业用以提升用户体验的重要工具。产品帮助中心是一个集中的信息库&#xff0c;为用户提供关于产品功能、故障排除、常见问题解答…

11.10

.text .global _start _start: 1.RCC时钟使能GPIOE RCC_MP_AHB4ENSETR[4]->1 LDR R0,0x50000a28 LDR R1,[R0] ORR R1,R1,#(0x3<<4) ORR R1,R1,#(0x1<<1) STR R1,[R0] 2.设置PE10为输出模式 GPIOE_MODER[21:20]->01 先清0 LDR R0,0x50006000 LDR R1,[R0]…

思科C9300交换机堆叠

思科C9300交换机堆叠仅支持同质堆叠&#xff0c;即仅以Cisco Catalyst 9300系列交换机作为堆叠成员的Cisco Catalyst 930O系列交换机堆叠。需要专用电缆&#xff0c;除数据交接支持堆叠外&#xff0c;还支持电源的堆叠。 在所有交换机上运行命令 "switch convert mode sta…

金融帝国实验室(Capitalism Lab)官方正版游戏『最新销售政策』

「金融帝国实验室」&#xff08;Capitalism Lab&#xff09;Enlight 官方正版游戏「2023双11特卖」 ■优惠时限&#xff1a;2023.11.01&#xff5e;11.30 ■游戏开发商&#xff1a;Enlight Software Ltd. 请您认准以下官方正版游戏购买链接&#xff1a;支持“支付宝&am…

如何在苹果iOS系统ipa应用中获取当前版本号和Bundle ID

在iOS应用开发过程中&#xff0c;了解如何获取和使用应用的当前版本号、Bundle ID和其他相关信息是至关重要的。无论是在应用内显示这些信息&#xff0c;还是在编写一些版本依赖的逻辑时&#xff0c;掌握这些知识点都将帮助开发者进行更有效的管理和维护。本文将详细介绍如何在…

数据分析是什么?

第一章- 数据分析是什么 数据分析是指 根据分析目的&#xff0c;用适当的分析方法及工具&#xff0c;对数据进行分析&#xff0c;提取有价值的信息&#xff0c;形成有效结论的过程。 数据分析的作用 通过观察数据&#xff0c;知道当前发生什么&#xff1f;通过具体的数据拆解…

Matplotlib数据可视化综合应用Matplotlib图形配置在线闯关_头歌实践教学平台

Matplotlib数据可视化综合应用图形配置 第1关 配置颜色条第2关 设置注释第3关 自定义坐标刻度第4关 配置文件与样式表 第1关 配置颜色条 任务描述 本关任务&#xff1a;使用colorbar绘制一个热成像图。 编程要求 在右侧编辑器Begin-End处补充代码&#xff0c;根据输入数据绘制…

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

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

3 Paimon数据湖中的表类型详解

更多Paimon数据湖内容请关注&#xff1a;https://edu.51cto.com/course/35051.html Paimon中支持多种表类型&#xff0c;下面我们来看一下。 3.1 全局维度 首先从全局维度来看&#xff0c;Paimon中的表类型可以大致划分为4种&#xff1a; 内部表、外部表、分区表和临时表。 …

接收表单数据

如果您尝试按下提交按钮&#xff0c;浏览器将显示“Method Not Allowed”错误。这是因为到目前为止&#xff0c;前一节中的登录视图函数完成了一半的工作。它可以在网页上显示表单&#xff0c;但是还没有逻辑来处理用户提交的数据。这是Flask-WTF使工作变得非常简单的另一个领域…

Python语法基础(字符串 列表 元组 字典)

目录 字符串(str)字符串的创建特殊情况字符串的转义字符字符串的运算符字符串常用方法求字符串长度去掉多余空格是否包含某子串分割字符串合并字符串替换字符串统计统计字符串出现的次数 练习&#xff1a;判断字符串是否为回文串 列表(list)列表的创建列表常用方法遍历列表列表…

redis配置文件详解

一、配置文件位置 以配置文件启动 Redis 的配置文件位于 Redis 安装目录下,文件名为 redis.conf ( Windows名为redis.windows. conf) 例: # 这里要改成你自己的安装目录 cd ./redis-6.0.8 vim redis.conf redis对配置文件对大小写不敏感 二、配置文件 1、获取当前服务的…

2023.11.10联赛 T4题解

题目大意 题目思路 我们考虑分块处理。 我们可以维护一个状态&#xff0c;表示块内每个字母对应的真实字母&#xff0c;因为只有 3 3 3个字母&#xff0c;所以只有 6 6 6种情况。 对于每一个块&#xff0c;我们可以对于每种状态、每种块&#xff0c;预处理出以 A A A或 B B …

文件缓存的读写

文件系统的读写&#xff0c;其实就是调用系统函数 read 和 write。下面的代码就是 read 和 write 的系统调用&#xff0c;在内核里面的定义。 SYSCALL_DEFINE3(read, unsigned int, fd, char __user *, buf, size_t, count) {struct fd f fdget_pos(fd); ......loff_t pos f…