简体   繁体   English

.NET BackGroundWorker - InvalidOperationException:跨线程操作无效

[英].NET BackGroundWorker - InvalidOperationException : Cross-thread operation not valid

I have a project coded in .NET Winforms. 我有一个用.NET Winforms编码的项目。 I need to implement a data-mining operation, print the text to TextBox and update the progress. 我需要实现数据挖掘操​​作,将文本打印到TextBox并更新进度。

I tried to use BackgroundWorker to do, but it throws a InvalidOperationException ( Cross-thread operation not valid: Control 'xxxxx' accessed from a thread other than the thread it was created on ) 我尝试使用BackgroundWorker来做,但它抛出一个InvalidOperationException( 跨线程操作无效:控件'xxxxx'从一个线程访问,而不是它创建的线程

To narrow down the potential causes of the problem, I started a new project, included the following: Button - To start the BackgroundWorker Label - to print the text. 为了缩小问题的潜在原因,我开始了一个新项目,包括以下内容:按钮 - 启动BackgroundWorker标签 - 打印文本。 And ProgressBar. 和ProgressBar。

However, the result is the same. 但是,结果是一样的。 I searched on SOF, and was told to use a delegate, but I am not familiar with it. 我搜索了SOF,并被告知要使用代表,但我不熟悉它。

This is the code sample that throws the error: 这是抛出错误的代码示例:

using System;
using System.Collections.Generic;
using System.ComponentModel;

namespace TestProject
{
    public partial class Form1 : Form
    {
        private readonly BackgroundWorker _bw = new BackgroundWorker();

        public Form1()
        {
            InitializeComponent();
            _bw.DoWork += RosterWork;
            _bw.ProgressChanged += BwProgressChanged;
            _bw.RunWorkerCompleted += BwRunWorkerCompleted;
            _bw.WorkerReportsProgress = true;
            _bw.WorkerSupportsCancellation = false;
        }

        private void RosterWork(object sender, DoWorkEventArgs doWorkEventArgs)
        {
            for (int i = 0; i < 1000; i++)
            {
                label1.Text = i.ToString();
                _bw.ReportProgress(Convert.ToInt32((i * (100 / 1000))));
            }
        }

        private void BwProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            progressBar1.Value = e.ProgressPercentage;
        }

        private void btnStart_Click(object sender, EventArgs e)
        {
            progressBar1.Show();
            _bw.RunWorkerAsync();
        }

        private void BwRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            progressBar1.Hide();
        }

    }
}

Update: I follow Jon Skeet's answer, it really work on my test project, but back to my real project, 更新:我遵循Jon Skeet的回答,它确实适用于我的测试项目,但回到我的真实项目,

The Layout of my Form: 我的表格布局:

Form - TabControl - Tab1 -Tab1Panel -TextBox1 表单 - TabControl - Tab1 -Tab1Panel -TextBox1

When reach this line : 到达此行时:

TextBox txtbox1 = new TextBox();
Tab1Panel.Controls.Add(txtbox1);

The error still occur when i add Textbox to Panel Control Programmatically. 当我以编程方式将Textbox添加到Panel Control时,仍会出现错误。

Finally,I replace by this: 最后,我替换为:

 if (Tab1Panel.InvokeRequired)
     Tab1Panel.Invoke((MethodInvoker)delegate { Tab1Panel.Controls.Add(txtbox1); });
 else
     Tab1Panel.Controls.Add(txtbox1);

Everything is work. 一切都很有效。 How to determine the control is InvokeRequired, Is it control specified? 如何确定控件是InvokeRequired,是否指定了控件?

This is the problem: 这就是问题:

label1.Text = i.ToString();

You're trying to change the label text within the BackgroundWorker , which is not running on a UI thread. 您正在尝试更改BackgroundWorker的标签文本,该文本未在UI线程上运行。 The point of BackgroundWorker is to do all the non-UI work there, using ReportProgress to periodically "go back" to the UI thread and update the UI with the progress you're making. BackgroundWorker是在那里完成所有非UI工作,使用ReportProgress定期“返回”UI线程并使用您正在进行的进度更新UI。

So either you need to change label1.Text in BwProgressChanged as well, or you need to use Control.Invoke / BeginInvoke just as you would from any other background thread: 因此您需要更改BwProgressChanged label1.Text或者您需要像使用任何其他后台线程一样使用Control.Invoke / BeginInvoke

// Don't capture a loop variable in a lambda expression...
int copy = i;
Action updateLabel = () => label1.Text = copy.ToString();
label1.BeginInvoke(updateLabel);

For more about the copying part, see Eric Lippert's blog post, "Closing over the loop variable considered harmful" . 有关复制部分的更多信息,请参阅Eric Lippert的博客文章“关闭循环变量被认为有害” In this particular case it's only an issue because I'm using BeginInvoke . 在这种特殊情况下,它只是一个问题,因为我正在使用BeginInvoke This could be changed to just: 可以改为:

Action updateLabel = () => label1.Text = i.ToString();
label1.Invoke(updateLabel);

... but now the background worker would always be waiting for the UI to catch up before it kept going, which in real life is usually not what you want. ...但是现在后台工作人员总是会等待UI继续前进,这在现实生活中通常不是你想要的。 I generally prefer BeginInvoke over Invoke . 我通常更喜欢BeginInvoke不是Invoke

use this code iw will work 使用这个代码我会工作

 BeginInvoke((MethodInvoker)delegate
               {
                   TextBox1.Text += "your text here";

               });

You're accessing your label - which has been created on the GUI thread - from within your backgroundworker-thread. 您正在从后台工作者线程中访问您的标签 - 已在GUI线程上创建。 Accessing a Windows control from a thread other than on which the control has been created, is not allowed; 不允许从创建控件之外的线程访问Windows控件; therefore you're given the exception. 因此你得到了例外。

You should not access the label directly, but instead you should raise an event from within your thread, - and make sure it's invoked on the correct thread - that signals the UI so that you can change the content of the label. 您不应该直接访问标签,而应该从您的线程中引发一个事件,并确保在正确的线程上调用它 - 它向UI发出信号,以便您可以更改标签的内容。

You must execute the methods of a control from the thread that has created them. 您必须从创建它们的线程中执行控件的方法。 To update them from a different thread use Invoke . 要从其他线程更新它们,请使用Invoke An example of how this can be done can be found here . 可以在此处找到如何完成此操作的示例。 You should also read Multithreading with Forms and Controls on MSDN. 您还应该在MSDN上阅读使用表单和控件的多线程

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 BackgroundWorker - 跨线程操作无效 - BackgroundWorker - Cross-thread operation not valid 具有ProgressBar的BackgroundWorker-跨线程操作无效 - BackgroundWorker with ProgressBar - Cross-thread operation not valid 我正在使用backgroundworker并得到:InvalidOperationException:跨线程操作无效-我应该如何处理? - I'm using backgroundworker and getting: InvalidOperationException: Cross-thread operation not valid - how should I handle it? C# BackgroundWorker 中的跨线程操作无效 - C# Cross-thread operation not valid in BackgroundWorker 错误:System.InvalidOperationException:跨线程操作无效 - Error :System.InvalidOperationException: Cross-thread operation not valid 跨线程操作无效 - Cross-thread operation not valid 跨线程操作无效 - Cross-thread operation not valid 尝试在Backgroundworker中获取comboBox的值时出现“跨线程操作无效”异常 - “Cross-thread operation is not valid” exception when trying to get value of comboBox in Backgroundworker C#-跨线程操作无效。 控制ListView…和背景工作人员 - C# - Cross-thread operation not valid. Control ListView…and backgroundworker 当我修改DataGridView时,它给system.invalidoperationexception跨线程操作无效吗? - When I modify DataGridView, It gives system.invalidoperationexception cross-thread operation not valid?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM