跟踪多个BackgroundWorkers

Joh*_*ell 6 .net c# wpf backgroundworker task-parallel-library

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

BackgroundWorkerWorkerReportsProgress = true这样的UI可以定期更新.该BackgroundWorkerWorkerSupportsCancellation = true,以便它可以被用户取消.一切都很好!

我在尝试实现第三个更复杂的行为时遇到了麻烦.基本上,用户需要具有BackgroundWorker在任何时间开始新任务的灵活性,包括当前正在执行的任务.如果任务当前正在执行并且启动了新任务,则需要将旧任务标记为Aborted.Aborted任务是从不同Cancelled的是Aborted不允许进行任何进一步的UI更新.应该"默默地取消".

我包裹BackgroundWorker里面AsyncTask类,并增加了IsAborted一点.检查IsAborted内部的位ProgressChangedRunWorkerCompleted防止进一步的UI更新.大!

但是,这种方法会崩溃,因为当启动新任务时,CurrentTask会替换为新的实例AsyncTask.结果,跟踪变得困难CurrentTask.

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

有没有更好的方法来跟踪多个AsyncTasks确保新的可以按需启动,旧的正确中止而没有进一步的UI更新?似乎没有一种很好的方法来跟踪CurrentTask ... TPL是否提供了更好的方法来处理我所追求的内容?

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

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;
}
Run Code Online (Sandbox Code Playgroud)

key*_*rdP 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);
Run Code Online (Sandbox Code Playgroud)

这样你就可以跟踪所有工人.由于List维护订单,您将知道最新的订单(列表中的最后一个),但Stack如果您愿意,也可以轻松使用此处.