Ignore the Tasks throwing Exceptions at Task.WhenAll and get only the completed results

后端 未结 3 1483
遥遥无期
遥遥无期 2021-02-19 01:17

I am working on a Task parallel problem that I have many Tasks that may or may not throw Exception.

I want to process all the tasks that finishes properly and log the re

相关标签:
3条回答
  • 2021-02-19 01:28

    You can get the result of each successfully completed Task<TResult> from its property Result.

    var NormalTask1 = Task.FromResult("Task result 1");
    var NormalTask2 = Task.FromResult("Task result 2");
    var ExceptionTk = Task.FromException<string>(new Exception("Bad Task"));
    
    Task<string>[] tasks = new[] { NormalTask1, NormalTask2, ExceptionTk };
    Task whenAll = Task.WhenAll(tasks);
    try
    {
        await whenAll;
    }
    catch
    {
        if (whenAll.IsFaulted) // there is also the possibility of being canceled
        {
            foreach (var ex in whenAll.Exception.InnerExceptions)
            {
                Console.WriteLine(ex); // Log each exception
            }
        }
    }
    
    string[] results = tasks
        .Where(t => t.Status == TaskStatus.RanToCompletion)
        .Select(t => t.Result)
        .ToArray();
    Console.WriteLine($"Results: {String.Join(", ", results)}");
    

    Output:

    System.Exception: Bad Task
    Results: Task result 1, Task result 2

    0 讨论(0)
  • 2021-02-19 01:32

    You can create a method like this to use instead of Task.WhenAll:

    public Task<ResultOrException<T>[]> WhenAllOrException<T>(IEnumerable<Task<T>> tasks)
    {    
        return Task.WhenAll(
            tasks.Select(
                task => task.ContinueWith(
                    t => t.IsFaulted
                        ? new ResultOrException<T>(t.Exception)
                        : new ResultOrException<T>(t.Result))));
    }
    
    
    public class ResultOrException<T>
    {
        public ResultOrException(T result)
        {
            IsSuccess = true;
            Result = result;
        }
    
        public ResultOrException(Exception ex)
        {
            IsSuccess = false;
            Exception = ex;
        }
    
        public bool IsSuccess { get; }
        public T Result { get; }
        public Exception Exception { get; }
    }
    

    Then you can check each result to see if it was successful or not.


    EDIT: the code above doesn't handle cancellation; here's an alternative implementation:

    public Task<ResultOrException<T>[]> WhenAllOrException<T>(IEnumerable<Task<T>> tasks)
    {    
        return Task.WhenAll(tasks.Select(task => WrapResultOrException(task)));
    }
    
    private async Task<ResultOrException<T>> WrapResultOrException<T>(Task<T> task)
    {
        try
        {           
            var result = await task;
            return new ResultOrException<T>(result);
        }
        catch (Exception ex)
        {
            return new ResultOrException<T>(ex);
        }
    }
    
    0 讨论(0)
  • 2021-02-19 01:44

    You can add HOC with exception handling and then check success.

     class Program
    {
        static async Task Main(string[] args)
        {
            var itemsToProcess = new[] { "one", "two" };
            var results = itemsToProcess.ToDictionary(x => x, async (item) =>
            {
                try
                {
                    var result = await DoAsync();
                    return ((Exception)null, result);
                }
                catch (Exception ex)
                {
                    return (ex, (object)null);
                }
            });
    
            await Task.WhenAll(results.Values);
    
            foreach(var item in results)
            {
                Console.WriteLine(item.Key + (await item.Value).Item1 != null ? " Failed" : "Succeed");
            }
        }
    
        public static async Task<object> DoAsync()
        {
            await Task.Delay(10);
            throw new InvalidOperationException();
        }
    }
    
    0 讨论(0)
提交回复
热议问题