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

C# .NET BackGroundWorker-InvalidOperationException:跨线程操作无效,c#,winforms,multithreading,C#,Winforms,Multithreading,我有一个用.NET Winforms编码的项目。 我需要执行数据挖掘操作,将文本打印到文本框并更新进度 我试图使用BackgroundWorker执行此操作,但它引发了InvalidOperationException(跨线程操作无效:控件“xxxxx”是从创建它的线程以外的线程访问的。) 为了缩小问题的潜在原因,我启动了一个新项目,包括以下内容: 按钮-启动BackgroundWorker 标签-打印文本。 还有ProgressBar 然而,结果是一样的。我在SOF上搜索,并被告知使用代理,但

我有一个用.NET Winforms编码的项目。 我需要执行数据挖掘操作,将文本打印到文本框并更新进度

我试图使用BackgroundWorker执行此操作,但它引发了InvalidOperationException(跨线程操作无效:控件“xxxxx”是从创建它的线程以外的线程访问的。

为了缩小问题的潜在原因,我启动了一个新项目,包括以下内容: 按钮-启动BackgroundWorker 标签-打印文本。 还有ProgressBar

然而,结果是一样的。我在SOF上搜索,并被告知使用代理,但我不熟悉它

这是引发错误的代码示例:

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();
        }

    }
}
当我以编程方式将Textbox添加到面板控件时,仍然会发生此错误

最后,我用以下内容代替:

 if (Tab1Panel.InvokeRequired)
     Tab1Panel.Invoke((MethodInvoker)delegate { Tab1Panel.Controls.Add(txtbox1); });
 else
     Tab1Panel.Controls.Add(txtbox1);
一切都是工作。如何确定是否需要调用控件,是否指定了控件?

这就是问题所在:

label1.Text = i.ToString();
您正在尝试更改
BackgroundWorker
中的标签文本,该标签文本未在UI线程上运行。
BackgroundWorker
的要点是在那里完成所有非UI工作,使用
ReportProgress
定期“返回”UI线程,并使用您正在进行的进度更新UI

因此,您需要在
BwProgressChanged
中更改
label1.Text
,或者您需要使用
控件。调用
/
BeginInvoke
,就像从任何其他后台线程调用一样:

// Don't capture a loop variable in a lambda expression...
int copy = i;
Action updateLabel = () => label1.Text = copy.ToString();
label1.BeginInvoke(updateLabel);
有关复制部分的更多信息,请参阅Eric Lippert的博客文章。在这种特殊情况下,这只是一个问题,因为我使用的是
BeginInvoke
。这可以更改为:

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

。。。但现在,后台工作人员总是在等待UI跟上进度后再继续工作,这在现实生活中通常不是您想要的。我通常更喜欢
BeginInvoke
而不是
Invoke

您正在从backgroundworker线程中访问您的标签(已在GUI线程上创建)。 不允许从创建控件的线程以外的线程访问Windows控件;因此,你得到了一个例外


您不应该直接访问标签,而是应该从线程中引发一个事件,并确保在正确的线程上调用该事件,该事件向UI发出信号,以便您可以更改标签的内容。

您必须从创建标签的线程执行控件的方法。要从不同的线程更新它们,请使用。可以找到如何做到这一点的示例。您还应该阅读MSDN。

使用此代码iw将起作用

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

               });

谢谢Jon Skeet!我在谷歌上搜索.NET线程。我读了一些关于Thread/ThreadStart/ThreadPool/Delegate的文章。看起来它们更像是线程编程的预期标准。那么使用BackgroundWorker是正确的吗?@Shiba:恐怕我不太理解你的评论,尤其是“所有这些都更像预期的标准”。。。
 BeginInvoke((MethodInvoker)delegate
               {
                   TextBox1.Text += "your text here";

               });