に長い実行中のタスクの中止当社のアプリケーションは、(潜在的に)長い作業単位を実行しているシリアル化するためにTPLを使用しています。作業(タスク)の作成はユーザ主導であり、いつでもキャンセルすることができます。応答性の高いユーザーインターフェースを得るために、現在の作業がもはや必要でない場合、私たちが行っていたことを断念し、直ちに別の作業を開始したいと考えています。それは常にtoken.IsCancellationRequested
の状態をチェックし、/場合は、キャンセルが検出された場合に救済するように単純ではありませんので、DoWork
方法は、長時間実行されているコールが含まれていはTPL
private Task workQueue;
private void DoWorkAsync
(Action<WorkCompletedEventArgs> callback, CancellationToken token)
{
if (workQueue == null)
{
workQueue = Task.Factory.StartWork
(() => DoWork(callback, token), token);
}
else
{
workQueue.ContinueWork(t => DoWork(callback, token), token);
}
}
:
タスクは、このような何かをキューに登録されています。長時間実行されている作業は、タスクがキャンセルされてもタスクが継続するまで続行をブロックします。
私はこの問題を回避するために2つのサンプル方法を思い付くが、どちらかが適切であることを確信していないですしています。私は彼らがどのように動作するかを実演するための簡単なコンソールアプリケーションを作成しました
重要な点は、は、元のタスクが完了する前に継続が発生することです。。
試み#1:内部タスク
static void Main(string[] args)
{
CancellationTokenSource cts = new CancellationTokenSource();
var token = cts.Token;
token.Register(() => Console.WriteLine("Token cancelled"));
// Initial work
var t = Task.Factory.StartNew(() =>
{
Console.WriteLine("Doing work");
// Wrap the long running work in a task, and then wait for it to complete
// or the token to be cancelled.
var innerT = Task.Factory.StartNew(() => Thread.Sleep(3000), token);
innerT.Wait(token);
token.ThrowIfCancellationRequested();
Console.WriteLine("Completed.");
}
, token);
// Second chunk of work which, in the real world, would be identical to the
// first chunk of work.
t.ContinueWith((lastTask) =>
{
Console.WriteLine("Continuation started");
});
// Give the user 3s to cancel the first batch of work
Console.ReadKey();
if (t.Status == TaskStatus.Running)
{
Console.WriteLine("Cancel requested");
cts.Cancel();
Console.ReadKey();
}
}
これは動作しますが、 "innerT" タスクは、私にとって非常にkludgey感じています。また、新しいタスク内のすべての実行時間の長い呼び出しの最大ラッピングを必要とすることによって、このように作業をキューに私のコードのすべての部分をリファクタリングするために私を強制するという欠点を持っています。
試み#2:、私はそれは結果だ使ったことがないことでTaskCompletionSourceを乱用してるよう
a)のそれは感じている:TaskCompletionSourceは
static void Main(string[] args)
{ var tcs = new TaskCompletionSource<object>();
//Wire up the token's cancellation to trigger the TaskCompletionSource's cancellation
CancellationTokenSource cts = new CancellationTokenSource();
var token = cts.Token;
token.Register(() =>
{ Console.WriteLine("Token cancelled");
tcs.SetCanceled();
});
var innerT = Task.Factory.StartNew(() =>
{
Console.WriteLine("Doing work");
Thread.Sleep(3000);
Console.WriteLine("Completed.");
// When the work has complete, set the TaskCompletionSource so that the
// continuation will fire.
tcs.SetResult(null);
});
// Second chunk of work which, in the real world, would be identical to the
// first chunk of work.
// Note that we continue when the TaskCompletionSource's task finishes,
// not the above innerT task.
tcs.Task.ContinueWith((lastTask) =>
{
Console.WriteLine("Continuation started");
});
// Give the user 3s to cancel the first batch of work
Console.ReadKey();
if (innerT.Status == TaskStatus.Running)
{
Console.WriteLine("Cancel requested");
cts.Cancel();
Console.ReadKey();
}
}
をいじっ再び、これは動作しますが、今私は2つの問題を抱えています私が仕事を終えたときにnullを設定するだけです。適切に私は前作のユニークなTaskCompletionSourceの単位ではなく、そのために作成されたタスクのハンドルを維持するために必要な継続を配線するために
B)。これは技術的に可能ですが、やはり厄介で奇妙な感じです。
ここからどこへ行く ?
私の質問は、これらの方法のどちらかがこの問題に取り組むための「正しい」方法であるか、より長い間実行されているタスクを早めに中止して直ちに開始するより正確で洗練されたソリューションです継続?私の好みは、インパクトの低いソリューションですが、それが正しいことであれば、巨大なリファクタリングを引き受けることができます。
代わりには、TPLは仕事のためにも、正しい道具である、または私はより良い、タスクキューイングメカニズムをしないのです。私のターゲットフレームワークは.NET 4.0です。
私もこちらに質問を:http://social.msdn.microsoft.com/Forums/en/parallelextensions/thread/d0bcb415-fb1e-42e4-90f8-c43a088537fb –