簡體   English   中英

BackgroundWorker - 跨線程操作無效

[英]BackgroundWorker - Cross-thread operation not valid

我有一個winform應用程序(一個表單),在這個表單上有一個RichTextBox。 在這個窗體的構造函數中,我創建了一個MyClass類的實例。 在“Form_Load”中,我從MyClass實例調用Initialisation方法。

在表單構造函數中

myClass = new MyClass(RichTextBox richTextBox);

在Form_Load中

myClass.Initialisation();

Initialisation方法中,在循環中,我讀了一些參數做其他的東西。 要不凍結應用程序(因為某些操作可能需要一段時間,幾秒鍾),我使用BackgroundWorker 我這樣使用它(見下面的代碼)。

當我執行時,我收到此錯誤: 跨線程操作無效:控制'richTextBox'從其創建的線程以外的線程訪問

你能告訴我怎么解決這個問題嗎? 當我不訪問richTextBox時,工作完美

public Class MyClass
{
    static BackgroundWorker _bw;
    public MyClass()
    {
        _bw = new BackgroundWorker
        {
            WorkerReportsProgress = true,
            WorkerSupportsCancellation = true
        };
        _bw.DoWork += bw_DoWork;
        _bw.ProgressChanged += bw_ProgressChanged;
        _bw.RunWorkerCompleted += bw_RunWorkerCompleted;
    }
    static void bw_DoWork(object sender, DoWorkEventArgs e)
    {
        foreach (....)
        {
            if (....)
            {
                richtextBox.Text.AppendText("MyText");
            }
        }
        e.Result = true;
    }
    static void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e){}
    static void bw_ProgressChanged(object sender, ProgressChangedEventArgs e){}
}

使用BackgroundWorker並不能免除正常的線程規則 - 例如只有 UI線程才能訪問UI組件。

如果要從BackgroundWorker更新UI而不是使用進度/完成事件(在UI線程上引發),則需要像在其他情況下一樣使用Control.Invoke / Control.BeginInvoke 例如:

if (....)
{
    Action action = () => richtextBox.Text.Add("MyText");
    richtextBox.Invoke(action); // Or use BeginInvoke
}

試試這段代碼,

BeginInvoke((MethodInvoker)delegate
{
    richtextBox.Text.Add("MyText");
});

使用BackgroundWorker組件,只有ProgressChangedRunWorkerCompleted事件允許您調用UI控件上的方法/屬性(應始終在UI線程上完成)。 當您在非UI線程上運行的DoWork事件中更新UI時,您會收到此錯誤,如果您願意,您應該使用DoWork事件中的Invoke或BeginInvoke方法更新UI控件。

為了使它更干凈並且基於Jon Skeet的建議,我做了一個擴展方法,它做了同樣的事情,你可以將“ this Label control ”更改為this TextBox control或簡單地使用“ this Control control ”(並且基本上允許每個控件都是輕松更新):

internal static class ExtensionMethods
{
    /// <summary>
    /// Updates the label text while being used in a multithread app.
    /// </summary>
    /// <param name="control">The control.</param>
    /// <param name="text">The text.</param>
    internal static void UpdateThreadedText(this Label control, string text)
    {
        Action action = () => control.Text = text;
        control.Invoke(action);
    }

    /// <summary>
    /// Refreshes the threaded.
    /// </summary>
    /// <param name="control">The control.</param>
    internal static void RefreshThreaded(this Label control)
    {
        Action action = control.Refresh;
        control.Invoke(action);
    }
}

然后用法非常簡單:

this.yourLabelName.UpdateThreadedText("This is the text");
this.yourTextBoxName.UpdateThreadedText("This is the text");
this.yourControlName.UpdateThreadedText("This is the text");

要么

this.yourLabelName.RefreshThreaded();

很適合我:)

你也可以試試這個。

this.Invoke(new MethodInvoker(delegate()
{
    richtextBox.Text.Add("MyText");
}));

我認為錯誤在這一行停止:

richtextBox.Text.Add("MyText");

你的問題我跟這個類似:

BackgroundWorker OnWorkCompleted拋出跨線程異常

加:

e.Result = "MyText";

在你的bw_DoWork

richTextBox1.AppendText((string)e.Result);

在你的bw_RunWorkerCompleted

(改變它以適合您的代碼)

編輯:

如果在BackgroundWorker工作期間多次完成,您可以添加:

_bw.ReportProgress(0, "MyText");

bw_DoWork並:

richTextBox1.AppendText((string)e.UserState);

bw_ProgressChanged

暫無
暫無

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

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