簡體   English   中英

.NET BackGroundWorker - InvalidOperationException:跨線程操作無效

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

我有一個用.NET Winforms編碼的項目。 我需要實現數據挖掘操​​作,將文本打印到TextBox並更新進度。

我嘗試使用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();
        }

    }
}

更新:我遵循Jon Skeet的回答,它確實適用於我的測試項目,但回到我的真實項目,

我的表格布局:

表單 - TabControl - Tab1 -Tab1Panel -TextBox1

到達此行時:

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

當我以編程方式將Textbox添加到Panel Control時,仍會出現錯誤。

最后,我替換為:

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

一切都很有效。 如何確定控件是InvokeRequired,是否指定了控件?

這就是問題:

label1.Text = i.ToString();

您正在嘗試更改BackgroundWorker的標簽文本,該文本未在UI線程上運行。 BackgroundWorker是在那里完成所有非UI工作,使用ReportProgress定期“返回”UI線程並使用您正在進行的進度更新UI。

因此您需要更改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);

有關復制部分的更多信息,請參閱Eric Lippert的博客文章“關閉循環變量被認為有害” 在這種特殊情況下,它只是一個問題,因為我正在使用BeginInvoke 可以改為:

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

...但是現在后台工作人員總是會等待UI繼續前進,這在現實生活中通常不是你想要的。 我通常更喜歡BeginInvoke不是Invoke

使用這個代碼我會工作

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

               });

您正在從后台工作者線程中訪問您的標簽 - 已在GUI線程上創建。 不允許從創建控件之外的線程訪問Windows控件; 因此你得到了例外。

您不應該直接訪問標簽,而應該從您的線程中引發一個事件,並確保在正確的線程上調用它 - 它向UI發出信號,以便您可以更改標簽的內容。

您必須從創建它們的線程中執行控件的方法。 要從其他線程更新它們,請使用Invoke 可以在此處找到如何完成此操作的示例。 您還應該在MSDN上閱讀使用表單和控件的多線程

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM