I have this code:
var task1 = operation1();
var task2 = operation2();
var result1 = await task1;
var result2 = await task2;
I do also handle UnobservedTaskException
(by logging it). The problem that I face is that after task1 fails and first await results in exception, task2 completes in an error and then I have a log entry that I do not want to see as I will already log the first exception and at that point all subsequent exceptions are of no interest to me.
So I am wondering if there is a way to do something so that all tasks are "ignored" in a way after I get an exception.
I know I can use await Task.WhenAll
, but the downside is that I have to wait for all exceptions to happen, though I know for sure that after first task results in exception, I don't need to wait for the other task to complete as the whole operation already failed.
Another possible solution is to write try/catch and cancel all tasks, but that's a bit cumbersome.
P.S. The example is simplified, I do have multiple tasks running like that. So I am looking for a generic solution that would work for any number of tasks
If you don't await the task or explicitly check for exceptions, the exception is lost. If you await the task, its exception is rethrown. As a best practice, you should always await the call. By default, this message is a warning.
So, yes, you can await already completed tasks. Note that, if you call await several time, it will return it every time and by reference if it's not a value.
Run is misused to run IO blocking tasks. Although the code will work just fine (e.g UI not not freeze) but it is still a wrong approach. This is because Task. Run will still block a thread from thread pool the entire time until it finishes the method.
The await operator doesn't block the thread that evaluates the async method. When the await operator suspends the enclosing async method, the control returns to the caller of the method.
The current method calls an async method that returns a Task or a Task<TResult> and doesn't apply the Await operator to the result. The call to the async method starts an asynchronous task. However, because no Await operator is applied, the program continues without waiting for the task to complete. In most cases, that behavior isn't expected.
An exception that's raised in a method that returns a Task or Task<TResult> is stored in the returned task. If you don't await the task or explicitly check for exceptions, the exception is lost. If you await the task, its exception is rethrown. As a best practice, you should always await the call. By default, this message is a warning.
If you don't await the task or explicitly check for exceptions, the exception is lost. If you await the task, its exception is rethrown. As a best practice, you should always await the call. By default, this message is a warning. For more information about hiding warnings or treating warnings as errors, see Configuring Warnings in Visual Basic.
An asynchronous method awaits a Task directly. When an asynchronous method awaits a Task directly, continuation usually occurs in the same thread that created the task, depending on the async context. This behavior can be costly in terms of performance and can result in a deadlock on the UI thread.
As an alternative to Task.WhenAll
you can use a progressive approach with Task.WhenAny
.
When any of the Tasks finishes then you can examine the result and you can decide what to do next. (Please bear in mind that Task.WhenAny
does not throw exception even it is awaited) The great thing about this approach is that you can easily add throttling (control the degree of parallelism) to this.
static async Task ProgressiveAsyncForEach(int degreeOfParallelism, params Task[] tasks)
{
var toBeProcessedTasks = new HashSet<Task>();
var remainingTasksEnumerator = tasks.GetEnumerator();
void AddNextTask()
{
if (!remainingTasksEnumerator.MoveNext()) return;
var nextTaskToProcess = (Task)remainingTasksEnumerator.Current;
toBeProcessedTasks.Add(nextTaskToProcess);
}
//Initialize
while (toBeProcessedTasks.Count < degreeOfParallelism)
{
AddNextTask();
}
while (toBeProcessedTasks.Count > 0)
{
var processedTask = await Task.WhenAny(toBeProcessedTasks).ConfigureAwait(false);
if (!processedTask.IsCompletedSuccessfully)
{
Console.WriteLine("One of the task has failed");
//TODO: log first failed task
//CONSIDER: cancel all the remaining tasks
return;
}
toBeProcessedTasks.Remove(processedTask);
AddNextTask();
}
}
static async Task Main(string[] args)
{
await ProgressiveAsyncForEach(2, Faulty(), Fast(), Slow());
Console.WriteLine("Application finished");
}
static async Task Slow()
{
Console.WriteLine("Slow started");
await Task.Delay(1000);
Console.WriteLine("Slow finished");
}
static async Task Fast()
{
Console.WriteLine("Fast started");
await Task.Delay(500);
Console.WriteLine("Fast finished");
}
static async Task Faulty()
{
Console.WriteLine("Faulty started");
await Task.Delay(700);
throw new Exception();
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With