How to cancel and raise an exception on Task.WhenAll if any exception is raised?

后端 未结 1 646
野性不改
野性不改 2020-12-18 12:18

I am waiting on multiples task using Task.WhenAll. When one of them generates an exception I would like Task.WhenAll (or any other way of awaiting multiples tasks) to immedi

相关标签:
1条回答
  • 2020-12-18 12:55

    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);
    
    0 讨论(0)
提交回复
热议问题