如果在Task.WhenAll中引发任何异常,如何取消并引发异常?

5
我正在使用Task.WhenAll等待多个任务。当其中一个任务生成异常时,我希望Task.WhenAll(或任何其他等待多个任务的方式)立即取消其他任务并引发异常。
这是否可能?
提前感谢。

你能检查一下这个答案吗?https://dev59.com/0l4d5IYBdhLWcg3wDe71 - Everton Santos
2
那不是WhenAll的工作方式。你可能需要重新考虑一下你的方法。 - Peter Ritchie
@EvertonSantos 谢谢你的链接!不过我的情况有些不同,我需要监视异常。如果在一个任务中引发了异常,我需要取消所有的任务。 - Fernando Silva
1个回答

12

取消是协作的WhenAll 不能取消线程,但您可以向它们全部传递一个 CancellationToken,并在出现异常时触发该令牌。

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);

在方法内部,您需要将 token.ThrowIfCancellationRequested() 放入函数中以检查令牌并取消任务。

public async Task Func1Async(CancellationToken token)
{
    foreach(var item in GetItems1())
    {
         await item.ProcessAsync(token);
         token.ThrowIfCancellationRequested();
    }
}

注意:您可以通过创建扩展方法来清理代码

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;
    }
}

这将使代码看起来像:

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);

OP 询问的是 WhenAll 而不是 WaitAll - David Pine
@DavidPine 第一句话中有一个打字错误,如果您查看代码示例,它使用了WhenAll。 - Scott Chamberlain
我也是这么想的,不想在不确定意图的情况下编辑你的回答。 - David Pine
很有用!你在示例中忘记将扩展方法设置为静态的了。 - MHGameWork

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接