跟踪多个BackgroundWorkers

时间:2013-06-04 13:38:57

标签: c# .net wpf task-parallel-library backgroundworker

我有一个WPF应用程序,我正在使用BackgroundWorker组件从后端检索一些数据并在UI中显示它。

BackgroundWorkerWorkerReportsProgress = true,因此可以定期更新用户界面。 BackgroundWorker也有WorkerSupportsCancellation = true,以便用户可以取消它。一切都很好!

我无法尝试实施第三种更复杂的行为。基本上,用户需要具有在任何时间启动新BackgroundWorker任务的灵活性,包括当前正在执行的任务。如果任务当前正在执行并且启动了新任务,则需要将旧任务标记为AbortedAborted任务与Cancelled不同,因为Aborted不允许进行任何进一步的UI更新。它应该“默默地取消”。

我将BackgroundWorker包裹在AsyncTask类中并添加了IsAborted位。检查IsAbortedProgressChanged中的RunWorkerCompleted位会阻止进一步的UI更新。太好了!

但是,这种方法会中断,因为在启动新任务时,CurrentTask会被AsyncTask的新实例替换。因此,跟踪CurrentTask

变得很困难

如上所述,在TODO:中,我几乎想要等到CurrentTask完成中止后才开始新任务。但我知道这将提供糟糕的用户体验,因为UI线程将被阻止,直到旧任务完成。

是否有更好的方法来跟踪多个AsyncTasks,以确保可以按需启动新的private AsyncTask CurrentTask { get; set; } private class AsyncTask { private static int Ids { get; set; } public AsyncTask() { Ids = Ids + 1; this.Id = Ids; this.BackgroundWorker = new BackgroundWorker(); this.BackgroundWorker.WorkerReportsProgress = true; this.BackgroundWorker.WorkerSupportsCancellation = true; } public int Id { get; private set; } public BackgroundWorker BackgroundWorker { get; private set; } public bool IsAborted { get; set; } } void StartNewTask() { if (this.CurrentTask != null && this.CurrentTask.BackgroundWorker.IsBusy) { AbortTask(); //TODO: should we wait for CurrentTask to finish up? this will block the UI? } var asyncTask = new AsyncTask(); asyncTask.BackgroundWorker.DoWork += backgroundWorker_DoWork; asyncTask.BackgroundWorker.ProgressChanged += backgroundWorker_ProgressChanged; asyncTask.BackgroundWorker.RunWorkerCompleted += backgroundWorker_RunWorkerCompleted; AppendText("Starting New Task: " + asyncTask.Id); this.CurrentTask = asyncTask; asyncTask.BackgroundWorker.RunWorkerAsync(); } void AbortTask() { if (this.CurrentTask != null && this.CurrentTask.BackgroundWorker.IsBusy) { AppendText("Aborting Task " + this.CurrentTask.Id + "..."); this.CurrentTask.IsAborted = true; this.CurrentTask.BackgroundWorker.CancelAsync(); } } void CancelTask() { if (this.CurrentTask != null && this.CurrentTask.BackgroundWorker.IsBusy) { AppendText("Cancelling Task " + this.CurrentTask.Id + "..."); this.CurrentTask.BackgroundWorker.CancelAsync(); } } void backgroundWorker_DoWork(object sender, DoWorkEventArgs e) { var backgroundWorker = (BackgroundWorker)sender; for (var i = 0; i < 10; i++) { //check before making call... if (backgroundWorker.CancellationPending) { e.Cancel = true; return; } //simulate a call to remote service... Thread.Sleep(TimeSpan.FromSeconds(10.0)); //check before reporting any progress... if (backgroundWorker.CancellationPending) { e.Cancel = true; return; } backgroundWorker.ReportProgress(0); } } void backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e) { if (this.CurrentTask.IsAborted) return; AppendText("[" + DateTime.Now.ToString("MM/dd/yyyy HH:mm:ss") + "] " + "Progress on Task: " + this.CurrentTask.Id + "..."); } void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { if (this.CurrentTask.IsAborted) return; if (e.Cancelled) { AppendText("Cancelled Task: " + this.CurrentTask.Id); } else if (e.Error != null) { AppendText("Error Task: " + this.CurrentTask.Id); } else { AppendText("Completed Task: " + this.CurrentTask.Id); } //cleanup... this.CurrentTask.BackgroundWorker.DoWork -= backgroundWorker_DoWork; this.CurrentTask.BackgroundWorker.ProgressChanged -= backgroundWorker_ProgressChanged; this.CurrentTask.BackgroundWorker.RunWorkerCompleted -= backgroundWorker_RunWorkerCompleted; this.CurrentTask= null; } 并且旧的正确中止而无需进一步更新UI?似乎没有一种好的方法可以跟踪CurrentTask ... TPL是否提供了更好的方法来处理我所追求的内容?

以下是我在Window类中的重要摘要:

{{1}}

1 个答案:

答案 0 :(得分:11)

根据我的理解,你不想实际中止线程,你只是希望它继续静默工作(即不更新UI)?一种方法是保留BackgroundWorkers列表并删除它们的事件处理程序,如果它们要“中止”。

List<BackgroundWorker> allBGWorkers = new List<BackgroundWorker>();

//user creates a new bg worker.
BackgroundWorker newBGWorker = new BackgroundWorker();
//.... fill out properties


//before adding the new bg worker to the list, iterate through the list 
//and ensure that the event handlers are removed from the existing ones    
foreach(var bg in allBGWorkers)
{    
   bg.ProgressChanged -= backgroundWorker_ProgressChanged;
   bg.RunWorkerCompleted -= backgroundWorker_RunWorkerCompleted;
}

//add the latest bg worker you created
allBGWorkers.Add(newBGWorker);

通过这种方式,您可以跟踪所有工人。由于List维护订单,您将知道最新的订单(列表中的最后一个),但如果您愿意,可以在此处轻松使用Stack