Cancellation is coopertive the WhenAll
can't cancel the threads but you can pass all of them a CancellationToken
and fire the token when you get a exception.
CancellationTokenSource cts = new CancellationTokenSource();
var task1 = Func1Async(cts.Token);
task1.ContinueWith(task => cts.Cancel(), cts.Token, TaskContinuationOptions.OnlyOnFaulted, TaskScheduler.Default);
var task2 = Func2Async(cts.Token);
task2.ContinueWith(task => cts.Cancel(), cts.Token, TaskContinuationOptions.OnlyOnFaulted, TaskScheduler.Default);
var task3 = Func3Async(cts.Token);
task3.ContinueWith(task => cts.Cancel(), cts.Token, TaskContinuationOptions.OnlyOnFaulted, TaskScheduler.Default);
await Task.WhenAll(task1, task2, task3);
from inside the methods you will need to put token.ThrowIfCancellationRequested()
inside the functions to check the token and cancel the task
public async Task Func1Async(CancellationToken token)
{
foreach(var item in GetItems1())
{
await item.ProcessAsync(token);
token.ThrowIfCancellationRequested();
}
}
NOTE: You could clean up the code a bit by making a extension method
public static class ExtensionMethods
{
public static Task CancelOnFaulted(this Task task, CancellationTokenSource cts)
{
task.ContinueWith(task => cts.Cancel(), cts.Token, taskContinuationOptions.OnlyOnFaulted, TaskScheduler.Default);
return task;
}
public static Task<T> CancelOnFaulted<T>(this Task<T> task, CancellationTokenSource cts)
{
task.ContinueWith(task => cts.Cancel(), cts.Token, taskContinuationOptions.OnlyOnFaulted, TaskScheduler.Default);
return task;
}
}
This would make the code look like
CancellationTokenSource cts = new CancellationTokenSource();
var task1 = Func1Async(cts.Token).CancelOnFaulted(cts);
var task2 = Func2Async(cts.Token).CancelOnFaulted(cts);
var task3 = Func3Async(cts.Token).CancelOnFaulted(cts);
await Task.WhenAll(task1, task2, task3);
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…