Why is UnobservedTaskException thrown despite the observation when using async await? - c#

The following scenario runs under .NET 4.5 so any UnobservedTaskException does not terminate the process.
I have the habit of listening to any UnobservedTaskException thrown by having this execute at the start of my app:
private void WatchForUnobservedTaskExceptions()
{
TaskScheduler.UnobservedTaskException += (sender, args) =>
{
args.Exception.Dump("Ooops");
};
}
I also have a helper method for when I want to explicitly ignore any exceptions thrown by my tasks:
public static Task IgnoreExceptions(Task task)
=> task.ContinueWith(t =>
{
var ignored = t.Exception.Dump("Checked");
},
CancellationToken.None,
TaskContinuationOptions.ExecuteSynchronously,
TaskScheduler.Default);
So if I have the following code execute:
void Main()
{
WatchForUnobservedTaskExceptions();
var task = Task.Factory.StartNew(() =>
{
Thread.Sleep(1000);
throw new InvalidOperationException();
});
IgnoreExceptions(task);
GC.Collect(2);
GC.WaitForPendingFinalizers();
Console.ReadLine();
}
After we return from the Console.ReadLine() we will not see any UnobservedTaskException thrown which is what we expect.
However if I change the above task to start using async/await with everything else the same as before:
var task = Task.Factory.StartNew(async () =>
{
await Task.Delay(1000);
throw new InvalidOperationException();
});
Now we get the UnobservedTaskException thrown. Debugging the code reveals the continuation executes with the t.Exception being null.
How can I ignore the exceptions properly in both scenarios?

Either use
var task = Task.Factory.StartNew(async () =>
{
await Task.Delay(1000);
throw new InvalidOperationException();
}).Unwrap();
or
var task = Task.Run(async () =>
{
await Task.Delay(1000);
throw new InvalidOperationException();
});
See this blogpost about Task.Run vs Task.Factory.StartNew about using Task.Factory.StartNew with async modifiers
By using the async keyword here, the compiler is going to map this delegate to be a Func<Task<int>>: invoking the delegate will return the Task<int> to represent the eventual completion of this call. And since the delegate is Func<Task<int>>, TResult is Task<int>, and thus the type of ‘t’ is going to be Task<Task<int>>, not Task<int>.
To handle these kinds of cases, in .NET 4 we introduced the Unwrap method.
Some more background
Why Not to Use Task.Factory.StartNew?
.. Does not understand async delegates. … . The problem is that when you pass an async delegate to StartNew, it’s natural to assume that the returned task represents that delegate. However, since StartNew does not understand async delegates, what that task actually represents is just the beginning of that delegate. This is one of the first pitfalls that coders encounter when using StartNew in async code.
EDIT
The type of task in var task = Task.Factory.StartNew(async (...)) => is actually Task<Task<int>>. You have to Unwrap that to get the source task. With that in mind:
You can only call Unwrap on a Task<Task>> so you could add an overload to IgnoreExceptions to accommodate that:
void Main()
{
WatchForUnobservedTaskExceptions();
var task = Task.Factory.StartNew(async () =>
{
await Task.Delay(1000);
throw new InvalidOperationException();
});
IgnoreExceptions(task);
GC.Collect(2);
GC.WaitForPendingFinalizers();
Console.ReadLine();
}
private void WatchForUnobservedTaskExceptions()
{
TaskScheduler.UnobservedTaskException += (sender, args) =>
{
args.Exception.Dump("Ooops");
};
}
public static Task IgnoreExceptions(Task task)
=> task.ContinueWith(t =>
{
var ignored = t.Exception.Dump("Checked");
},
CancellationToken.None,
TaskContinuationOptions.ExecuteSynchronously,
TaskScheduler.Default);
public static Task IgnoreExceptions(Task<Task> task)
=> task.Unwrap().ContinueWith(t =>
{
var ignored = t.Exception.Dump("Checked");
},
CancellationToken.None,
TaskContinuationOptions.ExecuteSynchronously,
TaskScheduler.Default);

Combination of var and Task and Task<T>'s interited relationship, masks the problem. If I rewrite the code slightly, it will become obvious where the problem is.
Task<int> task1 = Task.Factory.StartNew(() =>
{
Thread.Sleep(1000);
throw new InvalidOperationException();
return 1;
});
Task<Task<int>> task2 = Task.Factory.StartNew(async () =>
{
await Task.Delay(1000);
throw new InvalidOperationException();
return 1;
});
This better illustrates what Peter Bons is talking about.

Related

Switch new Task(()=>{ }) for Func<Task>

In an answer to one of my other questions, I was told that use of new Task(() => { }) is not something that is a normal use case. I was advised to use Func<Task> instead. I have tried to make that work, but I can't seem to figure it out. (Rather than drag it out in the comments, I am asking a separate question here.)
My specific scenario is that I need the Task to not start right when it is declared and to be able to wait for it later.
Here is a LinqPad example using new Task(() => { }). NOTE: This works perfectly! (Except that it uses new Task.)
static async void Main(string[] args)
{
// Line that I need to swap to a Func<Task> somehow.
// note that this is "cold" not started task
Task startupDone = new Task(() => { });
var runTask = DoStuff(() =>
{
//+++ This is where we want to task to "start"
startupDone.Start();
});
//+++ Here we wait for the task to possibly start and finish. Or timeout.
// Note that this times out at 1000ms even if "blocking = 10000" below.
var didStartup = startupDone.Wait(1000);
Console.WriteLine(!didStartup ? "Startup Timed Out" : "Startup Finished");
await runTask;
Console.Read();
}
public static async Task DoStuff(Action action)
{
// Swap to 1000 to simulate starting up blocking
var blocking = 1; //1000;
await Task.Delay(500 + blocking);
action();
// Do the rest of the stuff...
await Task.Delay(1000);
}
I tried swapping the second line with:
Func<Task> startupDone = new Func<Task>(async () => { });
But then the lines below the comments with +++ in them don't work right.
I swapped the startupDone.Start() with startupDone.Invoke().
But startupDone.Wait needs the task. Which is only returned in the lambda. I am not sure how to get access to the task outside the lambda so I can Wait for it.
How can use a Func<Task> and start it in one part of my code and do a Wait for it in another part of my code? (Like I can with new Task(() => { })).
The code you posted cannot be refactored to make use of a Func<Task> instead of a cold task, because the method that needs to await the task (the Main method) is not the same method that controls the creation/starting of the task (the lambda parameter of the DoStuff method). This could make the use of the Task constructor legitimate in this case, depending on whether the design decision to delegate the starting of the task to a lambda is justified. In this particular example the startupDone is used as a synchronization primitive, to signal that a condition has been met and the program can continue. This could be achieved equally well by using a specialized synchronization primitive, like for example a SemaphoreSlim:
static async Task Main(string[] args)
{
var startupSemaphore = new SemaphoreSlim(0);
Task runTask = RunAsync(startupSemaphore);
bool startupFinished = await startupSemaphore.WaitAsync(1000);
Console.WriteLine(startupFinished ? "Startup Finished" : "Startup Timed Out");
await runTask;
}
public static async Task RunAsync(SemaphoreSlim startupSemaphore)
{
await Task.Delay(500);
startupSemaphore.Release(); // Signal that the startup is done
await Task.Delay(1000);
}
In my opinion using a SemaphoreSlim is more meaningful in this case, and makes the intent of the code clearer. It also allows to await asynchronously the signal with a timeout WaitAsync(Int32), which is not something that you get from a Task out of the box (it is doable though).
Using cold tasks may be tempting in some cases, but when you revisit your code after a month or two you'll find yourself confused, because of how rare and unexpected is to have to deal with tasks that may or may have not been started yet.
I always try my hardest to never have blocking behavior when dealing with anything async or any type that represents potential async behavior such as Task. You can slightly modify your DoStuff to facilitate waiting on your Action.
static async void Main(string[] args)
{
Func<CancellationToken,Task> startupTask = async(token)=>
{
Console.WriteLine("Waiting");
await Task.Delay(3000, token);
Console.WriteLine("Completed");
};
using var source = new CancellationTokenSource(2000);
var runTask = DoStuff(() => startupTask(source.Token), source.Token);
var didStartup = await runTask;
Console.WriteLine(!didStartup ? "Startup Timed Out" : "Startup Finished");
Console.Read();
}
public static async Task<bool> DoStuff(Func<Task> action, CancellationToken token)
{
var blocking = 10000;
try
{
await Task.Delay(500 + blocking, token);
await action();
}
catch(TaskCanceledException ex)
{
return false;
}
await Task.Delay(1000);
return true;
}
First, the type of your "do this later" object is going to become Func<Task>. Then, when the task is started (by invoking the function), you get back a Task that represents the operation:
static async void Main(string[] args)
{
Func<Task> startupDoneDelegate = async () => { };
Task startupDoneTask = null;
var runTask = await DoStuff(() =>
{
startupDoneTask = startupDoneDelegate();
});
var didStartup = startupDoneTask.Wait(1000);
Console.WriteLine(!didStartup ? "Startup Timed Out" : "Startup Finished");
}

How to capture AggregateException? [duplicate]

Just noticed strange thing: to catch exception in caller from new Task, lambda MUST be marked as async!? Is it really necessary even if delegate has no await operators at all?
try
{
//Task.Run(() => // exception is not caught!
Task.Run(async () => // unnecessary async!?!
{
throw new Exception("Exception in Task");
}).Wait();
}
catch (Exception ex)
{
res = ex.Message;
}
Why there is neccesary for async operator?
All documentation i can find tells that delegate must not return Void and Task must be awaited for exception to propogate up to caller.
Added full code:
class Program
{
static void Main(string[] args)
{
var p = new Program();
p.Run();
}
public void Run()
{
string result;
try
{
result = OnSomeEvent((s, ea) => RunSomeTask());
}
catch (Exception ex) // Try to catch unhandled exceptions here!
{
result = ex.Message;
}
Console.WriteLine(result);
Console.ReadKey();
}
// Some other Framework bult-in event (can not change signature)
public string OnSomeEvent(EventHandler e)
{
e.Invoke(null, new EventArgs());
return "OK";
}
private async Task RunSomeTask()
{
await Task.Run(async () => // do not need async here!!!
//await Task.Run(() => // caller do not catches exceptions (but must)
{
throw new Exception("Exception in Task1");
});
}
}
So the qestion is how to catche ex. without asyn keyword???
Methods that return Task - such as Task.Run or async methods - will place any exceptions on that returned Task. It's up to you to observe that exception somehow. Normally this is done with await, like this:
await Task.Run(() => { throw ... });
In your case, the problem is in this line:
result = OnSomeEvent((s, ea) => RunSomeTask());
In this code, RunSomeTask is returning a Task, and that Task is never awaited. In order to observe the exception, you should await that task.
When using async/await, exceptions are automatically unwrapped at the site of the await. When using a Task and .Wait(), any exception are wrapped when they come out of the Task, and thus getting information requires you to dig into the Task.Exception property, since they do not propagate up the call stack.
See https://dotnetfiddle.net/MmEXsT

Cancelling an entire task when a called method does not return

I have a method that registers a background task that looks like this:
//snippet from task builder method
try
{
cancellationTokenSource.CancelAfter(10000);
btr = Task.Run(() => registerTask(builder, btr,cancellationTokenSource.Token), cancellationTokenSource.Token).Result;
}
catch (OperationCanceledException) // something went wrong
{
return null;
}
private BackgroundTaskRegistration registerTask(BackgroundTaskBuilder builder, BackgroundTaskRegistration btr, CancellationToken token)
{
CancellationTokenSource newToken = new CancellationTokenSource();
Task cancelledCheck = Task.Run(() =>
{
while (true)
{
if (token.IsCancellationRequested)
{
newToken.Cancel();
token.ThrowIfCancellationRequested();
}
}
}, newToken.Token);
btr = Task.Run(()=> builder.Register(),token).Result;
return btr;
}
My issue is that sometimes the builder.Register() method does not return anything. It's probably a Windows bug of some sort; the Register() method never finishes internally. Indeed, after 10 seconds, the token.ThrowIfCancellationRequested() method is called, but it does not throw to the try-catch statement where it's called. Initially I was calling builder.Register() directly without a Task.Run() but it didn't work, and neither does this.
However, if I replace btr = Task.Run(() =>... with a Task.Delay(ms) instead, where ms > 10000, my intended effect happens.
Am I doing something wrong? Or is there a better way to do this? Basically I just need code that will make the registerTask() method return null when builder.Register() does not finish after a few seconds.
Replacing the code with something like this worked for me:
btr = null;
cancellationTokenSource.CancelAfter(10000);
Task registerTask = Task.Factory.StartNew(() =>
{
btr = builder.Register();
});
Task cancellationTask = Task.Factory.StartNew(() =>
{
while (true)
{
if (cancellationTokenSource.Token.IsCancellationRequested) break;
}
}, cancellationTokenSource.Token);
Task[] tasks = new Task[2] { cancellationTask, registerTask };
Task.WaitAny(tasks);
Instead of handling the error, getting a cancellation request will trigger one of the tasks to end, and when it ends I return the task registration variable whether it's null or not.

await Task.WhenAll(taskA, taskB) continues although taskA and taskB are in infinite loop (both with await in the middle)

await Task.WhenAll(a, b); continues albeit neither a nor b have completed (as they're in infinite loop)
public partial class App : Application, {
protected override async void OnStartup(StartupEventArgs e) {
var a = Task.Factory.StartNew(async () => {
while (true) {
Trace.WriteLine("A ->");
await Task.Delay(TimeSpan.FromSeconds(0.1));
Trace.WriteLine("-> A");
}
}, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.FromCurrentSynchronizationContext());
var b = Task.Factory.StartNew(async () => {
while (true) {
Trace.WriteLine("B ->");
await Task.Delay(TimeSpan.FromSeconds(0.09));
Trace.WriteLine("-> B");
}
}, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.FromCurrentSynchronizationContext());
await Task.WhenAll(a, b);
// I expected that the following line will never run, but it does:
base.OnStartup(e);
}
}
Why is that?
edit:
Apparently the editor/plugin/whatever misguided me here:
await Task.WhenAll(a, b); continues albeit neither a nor b have completed
But they have completed. That's exactly why WhenAll is completing as well. You can see this yourself by simply inspecting the status of the tasks at that point in time.
Since you're passing an async method to StartNew, it's going to return a Task<Task>. The outer task will be completed as soon as it starts the inner task. It will not wait to be completed until the inner task finishes. While you could unwrap the tasks and pass those inner tasks to WhenAll, you could just as easily not wrap the tasks in the first place as you're not accomplishing anything by doing so.
Just invoke the async lambdas yourself to create the tasks that you want.
public partial class App : Application
{
protected override async void OnStartup(StartupEventArgs e)
{
Func<Task> a = async () =>
{
while (true)
{
Trace.WriteLine("A ->");
await Task.Delay(TimeSpan.FromSeconds(0.1));
Trace.WriteLine("-> A");
}
};
Func<Task> b = async () =>
{
while (true)
{
Trace.WriteLine("B ->");
await Task.Delay(TimeSpan.FromSeconds(0.09));
Trace.WriteLine("-> B");
}
};
await Task.WhenAll(a(), b());
base.OnStartup(e);
}
}
Don't use Task.Factory.StartNew. Use Task.Run instead.
Task.Factory.StartNew pre-dates async-await so it doesn't expect an async delegate (i.e. Func<Task>). In that case it returns a Task<T> where T is itself a Task. So a and b aren't Tasks, they are Task<Task>. Task.Factory.StartNew here only starts a task that returns the actual task you want to wait for.
You can use Unwrap on a and b to return a task that represents the entire operation, meaning:
await Task.WhenAll(a.Unwrap(), b.Unwrap());
But, Task.Run is simpler and does that implicitly.

How to make Task.WaitAll() to break if any exception happened?

I want to make Task.WaitAll() to break out if any of the running tasks throws an exception, so that I don't have to wait for 60 seconds to finish. How do I achieve such behavior? If WaitAll() cannot achieve that, is there any other c# feature or workaround?
Task task1 = Task.Run(() => throw new InvalidOperationException());
Task task2 = ...
...
try
{
Task.WaitAll(new Task[]{task1, task2, ...}, TimeSpan.FromSeconds(60));
}
catch (AggregateException)
{
// If any exception thrown on any of the tasks, break out immediately instead of wait all the way to 60 seconds.
}
The following should do it without altering the code of the original tasks (untested):
static bool WaitAll(Task[] tasks, int timeout, CancellationToken token)
{
var cts = CancellationTokenSource.CreateLinkedTokenSource(token);
var proxyTasks = tasks.Select(task =>
task.ContinueWith(t => {
if (t.IsFaulted) cts.Cancel();
return t;
},
cts.Token,
TaskContinuationOptions.ExecuteSynchronously,
TaskScheduler.Current).Unwrap());
return Task.WaitAll(proxyTasks.ToArray(), timeout, cts.Token);
}
Note it only tracks faulted tasks (those which threw). If you need to track cancelled tasks as well, make this change:
if (t.IsFaulted || t.IsCancelled) cts.Cancel();
Updated, waiting on the task proxies is redundant here, as pointed out by #svick in the comments. He proposes an improved version: https://gist.github.com/svick/9992598.
One way of doing that is to use CancellationTokenSource. You create cancellationtokensource, and pass it as an argument to Task.WaitAll. The idea is to wrap your task in try/catch block, and in case of exception, call cancel on cancellationtokensource.
Here's sample code
CancellationTokenSource mainCancellationTokenSource = new CancellationTokenSource();
Task task1 = new Task(() =>
{
try
{
throw new Exception("Exception message");
}
catch (Exception ex)
{
mainCancellationTokenSource.Cancel();
}
}, mainCancellationTokenSource.Token);
Task task2 = new Task(() =>
{
Thread.Sleep(TimeSpan.FromSeconds(3));
Console.WriteLine("Task is running");
}, mainCancellationTokenSource.Token);
task1.Start();
task2.Start();
Task.WaitAll(new[] { task1, task2},
6000, // 6 seconds
mainCancellationTokenSource.Token
);
}
catch (Exception ex)
{
// If any exception thrown on any of the tasks, break out immediately instead of wait all the way to 60 seconds.
}
Parallel class can do the job for you. You can use Parallel.For, ForEach or Invoke.
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Sample_04_04_2014_01
{
class Program
{
public static void Main(string[] args)
{
try
{
Parallel.For(0,20, i => {
Console.WriteLine(i);
if(i == 5)
throw new InvalidOperationException();
Thread.Sleep(100);
});
}
catch(AggregateException){}
Console.Write("Press any key to continue . . . ");
Console.ReadKey(true);
}
}
}
If one of these tasks throws an exception then no other task will be executed excepting those whose execution was already started. For, ForEach and Invoke are waiting for all tasks to complete before to resume control to the calling code. You can have even a finer grain control if you use ParallelLoopState.IsExceptional. Parallel.Invoke is more suited for your case.
I wanted to suggest a slight modification to Noseratio's excellent answer above. In my case I needed to preserve the original exception thrown, and in a surrounding try/catch distinguish between cancelled and exception states.
public static void WaitUnlessFault( Task[] tasks, CancellationToken token )
{
var cts = CancellationTokenSource.CreateLinkedTokenSource(token);
foreach ( var task in tasks ) {
task.ContinueWith(t =>
{
if ( t.IsFaulted ) cts.Cancel();
},
cts.Token,
TaskContinuationOptions.ExecuteSynchronously,
TaskScheduler.Current);
}
try {
Task.WaitAll(tasks, cts.Token);
}
catch ( OperationCanceledException ex ) {
var faultedTaskEx = tasks.Where(t => t.IsFaulted)
.Select(t => t.Exception)
.FirstOrDefault();
if ( faultedTaskEx != null )
throw faultedTaskEx;
else
throw;
}
}

Categories

Resources