私はWPF
アプリケーションを持っており、BackgroundWorker
コンポーネントを使ってバックエンドからデータを取り出し、UIに表示しています。複数のBackgroundWorkersをトラッキングする
BackgroundWorker
はWorkerReportsProgress = true
であり、UIを定期的に更新することができます。 BackgroundWorker
にはWorkerSupportsCancellation = true
も含まれているため、ユーザーはキャンセルすることができます。すべてがうまくいく!
3番目の複雑な動作を実装しようとしています。基本的に、ユーザーは現在実行中のときを含め、いつでも新しいBackgroundWorker
タスクを開始する柔軟性が必要です。タスクが現在実行中で、新しいタスクが開始された場合、古いタスクはAborted
とマークする必要があります。 Aborted
のタスクがCancelled
と異なる場合は、Aborted
はそれ以上のUI更新を行うことができません。それは "黙って"キャンセルする必要があります。
AsyncTask
クラス内にBackgroundWorker
をラップし、IsAborted
ビットを追加しました。 ビットをProgressChanged
とRunWorkerCompleted
にチェックすると、それ以上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;
}
長いプロセスを実行したいが、ユーザーがこの長い実行中のプロセスを途中で終了して新しいプロセスを開始できるようにしたい場合は、これは正しいです ? – TheKingDave
はい。しかし、タスクは、CancelまたはAbortを介して終了することができます。キャンセルはユーザーによって呼び出されます。ここでは、スレッドは終了しますが、UIの更新は自由に終了します。 2番目のタスクが作成され、別のタスクが現在実行されている場合に開始されると、中止が呼び出されます。元のタスクを中止する必要があります。この場合、新しいタスクが優先され、古い中断されたタスクはUIを更新することができず、「サイレントモードで終了する」必要があります。 –
したがって、システムが長時間実行しているプロセスを終了するかどうか、タスクを「中止する」としたら?私がユーザーで、中止を選択した場合は、キャンセルを選択するとシステムがすべて停止することが期待されます。正常に停止すると予想します。 – TheKingDave