Reputation: 39
I want a C# console app that
The below code deals with the cancellation correctly, but how to make unhandled exceptions in one task cleanly cancel the other tasks?
public static async Task<int> Main ()
{
CancellationTokenSource cts = new();
Console.CancelKeyPress += ( sender, e ) =>
{
e.Cancel = true;
cts.Cancel ();
};
try
{
var taskA= Task.Run( async () =>
{
...
}, cts.Token);
var taskB= Task.Run( async () =>
{
...
}, cts.Token);
var taskC= Task.Run( async () =>
{
...
}, cts.Token);
await Task.WhenAll ( [taskA,TaskB, TaskC] );
return 0;
}
catch ( OperationCanceledException )
{
.....
return 0;
}
catch ( Exception ex )
{
.......
return 1;
}
}
Upvotes: 1
Views: 67
Reputation: 143098
Arguably this is one of the rare case when it seems OKish to use ContinueWith
:
var startNew = Stopwatch.StartNew();
var cts = new CancellationTokenSource();
var task1 = Task.Run(async () =>
{
await Task.Delay(500, cts.Token);
throw new Exception("1");
});
var task2 = Task.Run(async () =>
{
await Task.Delay(50_000, cts.Token);
});
var tasks = new[] { task1, task2 };
// add continuation which will cancel token on fault of any task
foreach (var t in tasks)
{
_ = t.ContinueWith(_ => cts.Cancel(),
cts.Token,
TaskContinuationOptions.OnlyOnFaulted,
TaskScheduler.Default);
}
try
{
await Task.WhenAll(tasks); // wait for tasks
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
Console.WriteLine($"Elapsed: {startNew.ElapsedMilliseconds}"); // Elapsed: 530
Though I would consider using Parallel.ForEach(Async)
which supports fail fast out of the box AFAIK (though note that you might need to play with the level of parallelism). Code can look something like the following:
var cts = new CancellationTokenSource();
int[] delays = [500, 50_000];
try
{
await Parallel.ForEachAsync(delays, cts.Token, async (delay, ct) =>
{
await Task.Delay(delay, ct);
if (delay == delays.Min())
{
throw new Exception("1");
}
});
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
Console.WriteLine($"Elapsed:{startNew.ElapsedMilliseconds}"); // Elapsed:532
Notes:
Task.Run
actually handle the token since cancellation is cooperativeContinueWith
is quite confusing API and should be used with cautionUpvotes: 0