这个阻塞机制可以重新表述为:
Task.WhenAll(taskA, taskB, taskC).Wait()
这样可以返回一个任务,我们可以等待但也可以进行取消。因此,如果要忽略取消异常,可以执行以下操作:
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);
}
}
这将使您能够再次编写上述内容,而不会遇到OperationCancelledException
:
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();
}
}
希望能对您有所帮助。
CancellationToken
传递给WaitAll
函数呢? - Ivan StoevAggregateException
。不管你想不想要,这是他们的设计,你别无选择,只能使用try/catch
。 - Ivan Stoev