BackgroundWorker - 跨线程操作无效

时间:2012-01-05 07:00:21

标签: c# winforms

我有一个winform应用程序(一个表单),在这个表单上有一个RichTextBox。在此表单的构造函数中,我创建了类MyClass的实例。在“Form_Load”中,我从Initialisation实例调用方法MyClass

在表单构造函数

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){}
}

7 个答案:

答案 0 :(得分:32)

使用BackgroundWorker并不能免除正常的线程规则 - 例如只有 UI线程可以访问UI组件。

如果您想使用进度/完成事件(在UI线程上引发)从BackgroundWorker 而不是更新UI,则需要使用Control.Invoke / Control.BeginInvoke就像在其他情况下一样。例如:

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

答案 1 :(得分:12)

试试这段代码,

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

答案 2 :(得分:3)

使用BackgroundWorker组件,只有ProgressChangedRunWorkerCompleted事件允许您在UI控件上调用方法/属性(应始终在UI线程上完成)。当您更新在非UI线程上运行的DoWork事件中的UI时,您会收到此错误,如果您愿意,您应该使用DoWork事件中的Invoke或BeginInvoke方法更新UI控件。

答案 3 :(得分:1)

为了使其更清晰,并根据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();

很适合我:)

答案 4 :(得分:1)

您也可以试试这个。

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

答案 5 :(得分:0)

我认为错误在此行停止:

richtextBox.Text.Add("MyText");

你的问题我跟这个类似:

BackgroundWorker OnWorkCompleted throws cross-thread exception

答案 6 :(得分:0)

添加:

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