2016-11-14 7 views
1

私は同じCancellationTokenSourceで実行中のタスクのリストを持っています。Task.WaitAllはOperationCanceledExceptionをスローします

タスクがキャンセルされるまで、すべてのタスクが完了するまで現在のスレッドがまたはになるまで待ちます。

Task.WaitAll(tasks.ToArray(), searchCencellationTokenSource.Token); 
System.Console.WriteLine("Done !"); 

現在のスレッドが待機状態であっても、別のタスクによってタスクがキャンセルされることがあります。これは正常な動作です。

ただし、現在のスレッドが待機状態で、別のタスクがタスクをキャンセルしている間に、WaitAllはCancellationTokenSourceに「この操作はキャンセルされました」というメッセージをスローします。

私はそれがキャンセルされたことを知っています、私は意図的にそれをしました。例外がスローされることなく、タスクがキャンセルまたは完了した後、次のコードに進むことができます。

私はこのコードをtry & catchでラップすることができますが、例外をスローすることは重い操作であり、このような通常の動作では発生したくありません。これはあなたに私たちが待つことができますが、またの取り消しを管理することができ、タスクバックを与える

Task.WhenAll(taskA, taskB, taskC).Wait() 

+0

[ドキュメント](https://msdn.microsoft.com/en-us/library/dd321280(v=vs.110).aspx)です。あなたが例外を受けたくない場合、なぜ 'CancellationToken'を' WaitAll'に渡すのですか? –

+0

私はそれを渡して待機をキャンセルしますが、残念ながら、私はそれを渡さない場合でもこの例外をスローします。 @IvanStoev – Jacob

+4

少なくとも1つのタスクがキャンセルされた場合は、 'AggregateException'を取得することも記載されています。したいかどうか、これは彼らがそれを設計する方法であり、 'try/catch'を使う以外の選択肢はありません。 –

答えて

2

このブロック機構は、と言い換えることができます。

OperationCancelledExceptionをスローしません
Task.WhenAll(taskA, taskB, taskC).ContinueWith(t => { }, TaskContinuationOptions.OnlyOnCanceled).Wait(); 

:だから、キャンセル例外を無視して、次の操作を行うことができます。

次のようにこれは、その後、拡張メソッドにラップすることができます

あなたが OperationCancelledExceptionに遭遇することなく、再び、次のように書くことができるようになる
public static class TaskExtensions 
{ 
    public static Task IgnoreCancellation(this Task task) 
    { 
     return task.ContinueWith(t => { }, TaskContinuationOptions.OnlyOnCanceled); 
    } 
} 

を:

Task.WhenAll(taskA, taskB, taskC).IgnoreCancellation().Wait(); 

は、ここで示すテスト・フィクスチャですアプローチの仕組み:

public class IgnoreTaskCancellation 
{ 
    [Fact] 
    public void ShouldThrowAnAggregateException() 
    { 
     CancellationTokenSource cts = new CancellationTokenSource(10); 

     Task taskA = Task.Delay(20, cts.Token); 
     Task taskB = Task.Delay(20, cts.Token); 
     Task taskC = Task.Delay(20, cts.Token); 

     Assert.Throws<AggregateException>(() => Task.WhenAll(taskA, taskB, taskC).Wait()); 
    } 

    [Fact] 
    public void ShouldNotThrowAnException() 
    { 
     CancellationTokenSource cts = new CancellationTokenSource(10); 

     Task taskA = Task.Delay(20, cts.Token); 
     Task taskB = Task.Delay(20, cts.Token); 
     Task taskC = Task.Delay(20, cts.Token); 

     Task.WhenAll(taskA, taskB, taskC).ContinueWith(t => { }, TaskContinuationOptions.OnlyOnCanceled).Wait(); 
    } 

    [Fact] 
    public void ShouldNotThrowAnExceptionUsingIgnore() 
    { 
     CancellationTokenSource cts = new CancellationTokenSource(10); 

     Task taskA = Task.Delay(20, cts.Token); 
     Task taskB = Task.Delay(20, cts.Token); 
     Task taskC = Task.Delay(20, cts.Token); 

     Task.WhenAll(taskA, taskB, taskC).IgnoreCancellation().Wait(); 
    } 
} 

ps。

+0

あなたの答えをありがとう。私はこれがうまくいくと思います。おそらくあなたはそのようなコードを本番環境で実行していますか?もしそうなら、それはOKを実行しますか? – Jacob

+0

@Jacob設計上、それは例外をスローしますが、あなたはそれを望んでいません。だからあなたは本当に回避策を探しています。 – Steve

+0

それはうまく動作します、ありがとう! – Jacob

関連する問題