0

我一直在研究一种使用 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 事件在更新第一个值的 BackgroundWork 中调用,在 ping 完成时从 pingcompletedcallback 事件中调用。当 ProgressChanged 事件从 PingCompletedCallback 事件运行时,我只遇到了跨线程问题。它在更新第二个进度条时抛出错误。

我似乎无法弄清楚为什么其中一个电话会发生这种情况,而另一个电话不会。

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

如果是这样,我如何引发事件以便在 UI 线程而不是后台工作人员上处理它?

编辑:

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 是否可以访问后台工作程序的 ReportProgress 方法?

然后我可以更改 PingCompletedCallback:

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

到:

backgroundWorker1.ReportProgress(1, update);

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

感谢任何人的帮助。

编辑2:

Changed 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 语句的地方还是应该在新事件中使用 Invoke?

4

1 回答 1

4

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

您将在 Visual Studio 调试器的线程窗口中看到DoTask不在主线程上执行的;但是,当ReportProgress被调用时,处理程序在主线程上执行。由于您的控件可能是在主线程上创建的,因此您看不到异常。 在此处输入图像描述

现在,如果您尝试backgroundWorker1_ProgressChanged在 DoWork 方法中显式调用,backgroundWorker1_ProgressedChanged则将在执行 DoWork 方法的同一线程上执行,或者在您的情况下,是引发PingCompleted事件的方法: 在此处输入图像描述

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

编辑:

ReportProgress从处理程序调用PingCompleted将不起作用,因为您将丢失原始发件人。

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
}

编辑 2 回应:

private void PingUpdate (object sender, ProgressUpdated e)
{
     if (InvokeRequired)
     {
        Invoke(new Action<object, ProgressUpdated>(PingUpdate), sender, e);
        return;
     }

     toolStripStatusLabel1.Text = e.GeneralStatus;
     toolStripProgressBar2.Value = e.ProgressStatus;
     toolStripStatusLable3.Text = e.SepcificStatus;
}
于 2012-03-08T06:25:43.440 回答