BackgroundWorker和statusstrip更新的CrossThreading问题

时间:2012-03-08 05:29:52

标签: c# winforms backgroundworker multithreading statusstrip

我一直在研究一种使用BackgroundWorker定期执行ping操作的工具。我遇到了BackgroundWorker ProgressChanged事件的问题。 ProgressChanged事件的代码如下:

private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
       {
           ProgressUpdated update = (ProgressUpdated)e.UserState;
           if (sender.ToString() == "System.ComponentModel.BackgroundWorker")
           {
               toolStripStatusLabel1.Text = update.GeneralStatus;
               toolStripProgressBar1.Value = update.ProgressStatus;
               toolStripStatusLabel2.Text = update.SpecificStatus;
           }
           else
           {
               toolStripStatusLabel1.Text = update.GeneralStatus;
               toolStripProgressBar2.Value = update.ProgressStatus;
               toolStripStatusLabel3.Text = update.SpecificStatus;
           }
       }

ProgressChanged事件在后台工作中都会被调用,它会更新第一个值,并在ping完成时从pingcompletedcallback事件中调用。当ProgressChanged事件从PingCompletedCallback事件运行时,我只遇到交叉线程问题。它会在更新第二个进度条时抛出错误。

我似乎无法弄清楚为什么它会发生其中一个电话而不是另一个电话。

是否在BackgroundWorker线程上发生了PingCompletedCallBack,这就是导致交叉线程问题的原因?

如果是这样,我如何举起活动,以便在UI线程而不是背景工作者处理它?<​​/ p>

编辑:

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
    {
         BackgroundWorker worker = sender as BackgroundWorker;
         // creates ping and sends it async
         ProgressUpdated args = new ProgressUpdated(string1, int1, string 2);
         worker.ReportProgress(0,args);
         // rest of thread for cleanup when cancellation is called
    }
private void PingCompletedCallback(object sender, PingCompletedEventArgs e)
    {
            // handle the ping response
            ProgressUpdated update = new ProgressUpdated(string1, int1, string2);
            ProgressChangedEventArgs changed = new ProgressChangedEventArgs(1,update);
            backgroundWorker1_ProgressChanged(this, changed);
            // handle other types of responses

    }

我认为使用事件是为了允许线程分离。 Aka工作线程引发UI线程正在侦听的事件,然后在UI线程上处理引发的事件。

由于我的理解是错误的,PingCompletedCallBack是否可以访问backgroundworker的ReportProgress方法?

然后我可以在PingCompletedCallback中更改:

ProgressChangedEventArgs changed = new ProgressChangedEventArgs(1,update);
backgroundWorker1_ProgressChanged(this, changed);

为:

backgroundWorker1.ReportProgress(1, update);

还是我需要以其他方式改变它?

感谢任何人的帮助。

编辑2:

更改了ProgrssChanged事件

private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
       {
           ProgressUpdated update = (ProgressUpdated)e.UserState;
           toolStripStatusLabel1.Text = update.GeneralStatus;
           toolStripProgressBar1.Value = update.ProgressStatus;
           toolStripStatusLabel2.Text = update.SpecificStatus;
       }

然后我创建了第二个更新事件

private void PingUpdate (object sender, ProgressUpdated e)
    {
         toolStripStatusLabel1.Text = e.GeneralStatus;
         toolStripProgressBar2.Value = e.ProgressStatus;
         toolStripStatusLable3.Text = e.SepcificStatus;
    }

我唯一剩下的就是从PingCompletedCallback中调用新事件,使其在UI线程上执行。这是使用Invoke语句还是在新事件中使用Invokes?

1 个答案:

答案 0 :(得分:4)

BackgroundWorker的文档说明您不应该通过DoWork方法操作UI对象,并且应该通过ReportProgress对UI对象进行任何更改。我没有看过反射器,但它可能会为你执行隐藏的“调用”。无论是什么引发你的PingCompleted事件,都可能在工作线程或其他一些主线程的线程中执行。

您将在Visual Studio调试器的线程窗口中看到DoTask未在主线程上执行;但是,当调用ReportProgress时,处理程序在主线程上执行。由于您的控件可能是在主线程上创建的,因此您不会看到异常。 enter image description here

现在,如果您尝试在DoWork方法中显式调用backgroundWorker1_ProgressChanged,那么backgroundWorker1_ProgressedChanged将在执行DoWork方法的同一个线程上执行,或者,在您的情况下,将执行提升的方法PingCompleted事件: enter image description here

您可以通过在backgroundWorker1_ProgressChanged处理程序中添加InvokeRequired检查来解决此跨线程异常,或者路由PingCompleted处理程序以调用ReportProgress

编辑:

ReportProgress处理程序调用{​​{1}}将无效,因为您将丢失原始发件人。

PingCompleted

编辑2回复:

private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    if (InvokeRequired)
    {
        Invoke(new ProgressChangedEventHandler(backgroundWorker1_ProgressChanged), sender, e);
        return;
    }

    // The rest of your code goes here
}