skip to Main Content

Currently I have a code like this:

 bool task1Result = await RunTask1(data);
 if(!task1Result)
     return false;

 bool task2Result = await RunTask2(data);
 if(!task2Result)
     return false;

 bool task3Result = await RunTask3(data);
 if(!task3Result)
     return false;

 bool task4Result = await RunTask4(data);
 if(!task4Result)
     return false;

Added sample:

private async Task<bool> RunListOfTasks() {
CancellationTokenSource cts = new CancellationTokenSource();
CancellationToken ct = cts.Token;

var tasks = new List<Task<bool>> { RunTask1(data, ct), RunTask2(data, ct), RunTask3(data, ct), RunTask4(data, ct) };
while (tasks.Any())
{
    var currentTask = await Task.WhenAny(tasks);
    if (!await currentTask)
    {
        ct.Cancel();
        return false;
    }
    tasks.Remove(currentTask);
}
return true;
}

Is it possible to run all of them in parallel and if one of them fails (like result is false), then stop processing the rest and return. Thanks

2

Answers


  1. The Task.WhenAny-in-a-loop is generally considered an antipattern, because of its O(n²) complexity. The preferred approach is to wrap your tasks in another set of tasks, that will include the functionality of canceling the CancellationTokenSource when the result is false. Then await the wrapper tasks instead of the initial tasks, and propagate their result.

    An easy way to wrap the tasks is the Select LINQ operator:

    private async Task<bool> RunListOfTasks()
    {
        using CancellationTokenSource cts = new();
    
        List<Task<bool>> tasks = new()
        {
            RunTask1(data, cts.Token),
            RunTask2(data, cts.Token),
            RunTask3(data, cts.Token),
            RunTask4(data, cts.Token),
        };
    
        Task<bool>[] enhancedTasks = tasks.Select(async task =>
        {
            try
            {
                bool result = await task.ConfigureAwait(false);
                if (!result) cts.Cancel();
                return result;
            }
            catch (OperationCanceledException) when (cts.IsCancellationRequested)
            {
                return false;
            }
        }).ToArray();
    
        bool[] results = await Task.WhenAll(enhancedTasks).ConfigureAwait(false);
        return results.All(x => x);
    }
    
    Login or Signup to reply.
  2. Microsoft’s Reactive Framework does this in a very nice way:

    bool[] result =
        await 
            Observable
                .Merge(
                    Observable.FromAsync(ct => RunTask1(data, ct)),
                    Observable.FromAsync(ct => RunTask2(data, ct)),
                    Observable.FromAsync(ct => RunTask3(data, ct)),
                    Observable.FromAsync(ct => RunTask4(data, ct)))
                .TakeUntil(x => x == false)
                .ToArray();
    

    It returns all of the results that come in right up to the point one of the tasks returns a false.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search