我正在尝试在WPF应用程序中使用Background Worker。繁重的任务使用WebClient下载一些HTML并从中解析一些信息。理想情况下,我希望在不锁定UI的情况下进行下载和解析,并在完成工作后将结果放入UI中。
它工作正常,但是,如果我快速提交“下载和解析”命令,我会收到错误:
此BackgroundWorker当前正忙,无法运行多个任务 同时
所以我做了一些谷歌搜索,似乎我可以启用后台工作者的.WorkerSupportsCancellation
属性,只有.CancelAsync()
。但是,这不能按预期工作(取消当前的下载和解析)。
我仍然遇到上述错误。
这是我的代码:
//In window constructor.
_backgroundWorker.WorkerSupportsCancellation = true;
_backgroundWorker.DoWork += new DoWorkEventHandler(_backgroundWorker_DoWork);
_backgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(_backgroundWorker_RunWorkerCompleted);
//Declared at class level variable.
BackgroundWorker _backgroundWorker = new BackgroundWorker();
//This is the method I call from my UI.
private void LoadHtmlAndParse(string foobar)
{
//Cancel whatever it is you're doing!
_backgroundWorker.CancelAsync();
//And start doing this immediately!
_backgroundWorker.RunWorkerAsync(foobar);
}
POCOClassFoo foo = new POCOClassFoo();
void _backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
//This automagically sets the UI to the data.
Foo.DataContext = foo;
}
void _backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
//DOING THE HEAVY LIFTING HERE!
foo = parseanddownloadresult()!
}
答案 0 :(得分:6)
调用CancelAsync
仍然会触发RunWorkerCompleted
事件。在这种情况下,您需要检查CancelAsync
,确保未调用e.Cancelled
。在此事件触发之前,您无法拨打RunWorkerAsync
。
或者,我建议您按照Tigran的建议做,并每次创建一个新的BackgroundWorker
。
此外,我建议将_backgroundWorker_DoWork
的结果存储在e.Result
中,然后在_backgroundWorker_RunWorkerCompleted
也许是这样的
BackgroundWorker _backgroundWorker;
private BackgroundWorker CreateBackgroundWorker()
{
var bw = new BackgroundWorker();
bw.WorkerSupportsCancellation = true;
bw.DoWork += _backgroundWorker_DoWork;
bw.RunWorkerCompleted += new _backgroundWorker_RunWorkerCompleted;
return bw.
}
private void LoadHtmlAndParse(string foobar)
{
//Cancel whatever it is you're doing!
if (_backgroundWorer != null)
{
_backgroundWorker.CancelAsync();
}
_backgroundWorker = CreateBackgroundWorker();
//And start doing this immediately!
_backgroundWorker.RunWorkerAsync(foobar);
}
//you no longer need this because the value is being stored in e.Result
//POCOClassFoo foo = new POCOClassFoo();
private void _backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error != null)
{
//Error handling goes here.
}
else
{
if (e.Cancelled)
{
//handle cancels here.
}
{
//This automagically sets the UI to the data.
Foo.DataContext = (POCOClassFoo)e.Result;
}
}
private void _backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
//DOING THE HEAVY LIFTING HERE!
e.Result = parseanddownloadresult()!
}
答案 1 :(得分:5)
问题在于CancelAsync()
做了什么:以异步方式取消。这意味着它不会立即停止 ,但过了一段时间。那段时间永远无法计算或预测,所以你有几个选择:
等到这个backround worker
停止真的,等待一段时间,直到它的IsBusy属性变为false
或者,我认为,更好的解决方案是启动另一个background worker
,考虑到取消请求已经发送到第一个,因此它将很快或稍后停止。在这种情况下,您需要知道来自哪个 background worker
数据,以便处理它,在第二个数据开始时,第一个数据仍将运行并从{ {1}}。
希望这有帮助。
答案 2 :(得分:2)
CancelAsync在工作人员取消并停止工作之前返回。因此,您的RunWorkerAsync调用将在工作人员准备好之前启动,并且您将收到该错误。你需要等待工人先做好准备。
答案 3 :(得分:0)
当我对跟踪异步操作的进度不感兴趣时,我更倾向于只在ThreadPool.QueueUserWorkItem
打一个lambda,而不是实例化并设置一个后台工作器,我必须检查状态为能够以理智的方式重用。
答案 4 :(得分:0)
你需要在开始之前进行验证。
f( !bw.IsBusy )
bw.RunWorkerAsync();
else
MessageBox.Show("Can't run the bw twice!");
答案 5 :(得分:0)
您正在调用CancelAsync而不等待后台工作程序实际取消该工作。此外,您必须拥有自己的逻辑来取消工作。 MSDN上有一个很好的例子,展示了如何做到这一点。基本上在你的parseanddownloadresult()方法中,你需要检查CancellationPending属性。