我有一个WPF
应用程序,我使用BackgroundWorker
组件从后端检索一些数据并将其显示在UI中。跟踪多个背景工作者
BackgroundWorker
有WorkerReportsProgress = true
,这样就可以定期更新UI。 BackgroundWorker
也有WorkerSupportsCancellation = true
,以便它可以被用户取消。所有这一切都很好!
我在尝试实现第三个更复杂的行为时遇到了问题。基本上,用户需要有灵活性随时启动新的BackgroundWorker
任务,包括当前正在执行的任务。如果任务当前正在执行并且新任务已启动,则需要将旧任务标记为Aborted
。 Aborted
任务与Cancelled
不同,Aborted
不允许进行任何进一步的UI更新。它应该被“默默取消”。
我将BackgroundWorker
包装在AsyncTask
类中,并添加了IsAborted
位。检查ProgressChanged
和RunWorkerCompleted
中的IsAborted
位,以防止进一步的UI更新。大!
但是,这种方法发生故障,因为当新任务启动时,CurrentTask
被替换为AsyncTask
的新实例。因此,跟踪CurrentTask
变得很困难。
如上所述,在TODO:
,这几乎就像我想等到CurrentTask
在开始新任务之前中止后完成。但我知道这将提供糟糕的用户体验,因为UI线程将被阻塞,直到旧任务完成。
有没有更好的方法来跟踪多个AsyncTasks
确保新的可以按需启动和旧的正确中止没有进一步的UI更新?似乎没有一种跟踪CurrentTask的好方法...... TPL是否提供了一种更好的方式来处理我所追求的内容?
这里是著名的片断我有我的窗口类中:
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;
}
看起来像是这样,你想要运行一个漫长的过程,但允许用户杀死这个长期运行的过程中途,并开始一个新的。它是否正确 ? – TheKingDave
是的。但是,任务可以通过取消或通过中止来终止。取消是由用户调用的。在这里,线程退出,但它可以完成更新UI。当第二个任务被创建时调用Abort,并在另一个任务正在执行时被启动。原始任务必须中止。在这种情况下,新任务优先,旧中止任务无法更新用户界面,并且必须“静静地退出”。 –
因此,如果系统完成长时间运行的过程,如果“中止”任务?如果我是一个用户,我选择了中止,如果我选择取消,我希望系统能够停止所有事情,我希望它能够优雅地停止。 – TheKingDave