我一直在研究一种使用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?
答案 0 :(得分:4)
BackgroundWorker的文档说明您不应该通过DoWork方法操作UI对象,并且应该通过ReportProgress对UI对象进行任何更改。我没有看过反射器,但它可能会为你执行隐藏的“调用”。无论是什么引发你的PingCompleted
事件,都可能在工作线程或其他一些不主线程的线程中执行。
您将在Visual Studio调试器的线程窗口中看到DoTask
未在主线程上执行;但是,当调用ReportProgress
时,处理程序在主线程上执行。由于您的控件可能是在主线程上创建的,因此您不会看到异常。
现在,如果您尝试在DoWork方法中显式调用backgroundWorker1_ProgressChanged
,那么backgroundWorker1_ProgressedChanged
将在执行DoWork方法的同一个线程上执行,或者,在您的情况下,将执行提升的方法PingCompleted
事件:
您可以通过在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
}