次のようなタスクがあります。
var task = Task.Factory.StartNew <object>(LongMethod);
task.ContinueWith(TaskCallback, TaskScheduler.FromCurrentSynchronizationContext());
LongMethodは、実行時間の長いサービスを呼び出しますが、その間はキャンセルトークンを常にポーリングして、キャンセルされているかどうかを確認できません(少なくとも、できるとは思わない)。ただし、コールバックメソッドを「キャンセル」または無視することに関心があります。
TaskCallbackが呼び出されたとき、それが最新のタスクからのものである場合にのみ「結果」に関心があります(LongMethodが呼び出すサービスが順序を保持し、ユーザーがボタンを何度もクリックできると仮定します。ただし、関連するのは最新のものだけです)。
次の方法でコードを変更しました。タスクを作成した後、それをスタックの一番上に追加します。TaskCallbackで、コールバックに渡されたタスクが最新のものであるかどうかを確認します(つまり、スタックの最上位にあるTryPeek)。そうでない場合は、結果を無視します。
private ConcurrentStack<Task> _stack = new ConcurrentStack<Task>();
private void OnClick(object sender, ItemClickEventArgs e)
{
var task = Task.Factory.StartNew < object >( LongMethod);
task.ContinueWith(TaskCallback, TaskScheduler.FromCurrentSynchronizationContext());
_stack.Push(task);
}
private void TaskCallback(Task<object> task)
{
Task topOfStack;
if(_stack.TryPeek(out topOfStack)) //not the most recent
{
if (task != topOfStack) return;
}
//else update UI
}
これは「ベストプラクティス」のソリューションではないと確信しています。しかし、何ですか?キャンセルトークンの受け渡しと維持も、それほどエレガントではないようです。