简体   繁体   English

BackgroundWorker - 跨线程操作无效

[英]BackgroundWorker - Cross-thread operation not valid

I have a winform application (one form), on this form there is a RichTextBox. 我有一个winform应用程序(一个表单),在这个表单上有一个RichTextBox。 In the constructor of this form I create an instance of the class MyClass . 在这个窗体的构造函数中,我创建了一个MyClass类的实例。 In the “Form_Load” I call the method Initialisation from MyClass instance. 在“Form_Load”中,我从MyClass实例调用Initialisation方法。

In the form constructor 在表单构造函数中

myClass = new MyClass(RichTextBox richTextBox);

In the Form_Load 在Form_Load中

myClass.Initialisation();

In the Initialisation method, in a loop, I read some parmeters do other stuffs. Initialisation方法中,在循环中,我读了一些参数做其他的东西。 To not freeze the application (because some operation can take a while, some seconds), I use a BackgroundWorker . 要不冻结应用程序(因为某些操作可能需要一段时间,几秒钟),我使用BackgroundWorker I use it like this (see code below). 我这样使用它(见下面的代码)。

When I execute, I get this error : Cross-thread operation not valid: Control 'richTextBox' accessed from a thread other than the thread it was created on . 当我执行时,我收到此错误: 跨线程操作无效:控制'richTextBox'从其创建的线程以外的线程访问

Could you tell me how solve this ? 你能告诉我怎么解决这个问题吗? Work perfect when I don't access the 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){}
}

Using BackgroundWorker doesn't exempt you of the normal threading rules - such as that only the UI thread can access UI components. 使用BackgroundWorker并不能免除正常的线程规则 - 例如只有 UI线程才能访问UI组件。

If you want to update the UI from a BackgroundWorker other than using the progress/completion events (which are raised on the UI thread) you need to use Control.Invoke / Control.BeginInvoke just as you would in other situations. 如果要从BackgroundWorker更新UI而不是使用进度/完成事件(在UI线程上引发),则需要像在其他情况下一样使用Control.Invoke / Control.BeginInvoke For example: 例如:

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

try this code, 试试这段代码,

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

Using BackgroundWorker component, only the ProgressChanged and RunWorkerCompleted events allow you to invoke methods/properties on UI controls (which should be always done on UI thread). 使用BackgroundWorker组件,只有ProgressChangedRunWorkerCompleted事件允许您调用UI控件上的方法/属性(应始终在UI线程上完成)。 As you are updating the UI in DoWork event which runs on a non-UI thread you are getting this error, you should probably update you UI controls using Invoke or BeginInvoke methods in DoWork event if you want to. 当您在非UI线程上运行的DoWork事件中更新UI时,您会收到此错误,如果您愿意,您应该使用DoWork事件中的Invoke或BeginInvoke方法更新UI控件。

To make it cleaner and based on Jon Skeet's suggestion, I made an extension method which does the same, you can change the " this Label control " to this TextBox control or simply use " this Control control " (and basically allow every control to be updated easily): 为了使它更干净并且基于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);
    }
}

And then the usage is quite simple: 然后用法非常简单:

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

or 要么

this.yourLabelName.RefreshThreaded();

Works for me nicely :) 很适合我:)

You can also try this. 你也可以试试这个。

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

i think the error stops on this line: 我认为错误在这一行停止:

richtextBox.Text.Add("MyText");

your question i similar to this: 你的问题我跟这个类似:

BackgroundWorker OnWorkCompleted throws cross-thread exception BackgroundWorker OnWorkCompleted抛出跨线程异常

Add: 加:

e.Result = "MyText";

In your bw_DoWork And: 在你的bw_DoWork

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

In your bw_RunWorkerCompleted 在你的bw_RunWorkerCompleted

(Alter it to fit your code) (改变它以适合您的代码)

EDIT: 编辑:

If it's done many times during the BackgroundWorker 's work, you can add: 如果在BackgroundWorker工作期间多次完成,您可以添加:

_bw.ReportProgress(0, "MyText");

to the bw_DoWork and: bw_DoWork并:

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

to the bw_ProgressChanged . bw_ProgressChanged

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

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM