I am trying to get more familiar with async/await programming and exception handling.
static void Main(string[] args)
{
TestAsyncException();
}
private static async void TestAsyncException()
{
try
{
var result = await Task.Factory.StartNew(() => DoSomething());
//do something with the result
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
private static int DoSomething()
{
throw new Exception("Exception was thrown!");
}
I am expecting the Exception to be handled gracefully but instead the code execution stops and I get
An exception of type 'System.Exception' occurred in .. but was not
handled in user code.
But then when I continue executing the code the Exception actually gets caught (and the message is displayed to the Console).
How am I supposed to catch the Exception without breaking the execution of my code?
First of all, your exception is handled. I believe you are seeing the code stop execution and display the error because you have Break When Thrown on for exceptions. Check your Exception Window (Debug -> Windows -> Exception Settings).
When you use a return type of void on an async method, you lack the ability to get any sort of information back from the method - it's fire and forget. Except in specific situations, this is bad practice. Always have your async methods return a Task or Task<T>:
private static async Task TestAsyncException()
Now, your main method can listen to the task:
static void Main(string[] args)
{
TestAsyncException().Wait(); // or whatever you want to do with the task
Console.Read();
}
Normally, you could use await to unwrap the task here, but that's not allowed in the application entry point.
Related
So I read on async/await and somewhere, someplace I read that if you don't await an async method you basically lose it. It fires and forgets and goes into the AEeher and if it throws an exception - you will never know.
This was the example the author used:
static async void OnButtonClick()
{
yolo();
string imageUrl = null;
try
{
DownloadAndBlur(imageUrl);
}
catch (Exception ex)
{
Console.WriteLine($"Exception: {ex}");
}
Console.WriteLine("Done!");
}
static async Task DownloadAndBlur(string url)
{
if (url == null)
{
throw new ArgumentNullException(nameof(url));
}
}
Indeed, if I call the OnButtonClick() method from my code no exception gets thrown, or rather, nothing about an exception is printed on the console. While if I await the DownloadAndBlur method - an exception is written to the console.
So I tried to replicate the behaviour and wrote this:
static async void Execute()
{
Console.WriteLine(1);
yolo();
Console.WriteLine(2);
}
static Task yolo()
{
throw new Exception();
}
But an exception is thrown and my debugging session catches it. So what is different, because I think they are the same.
The Execute method is not fire-and-forget. It is async void.
static async void Execute()
{
YoloAsync();
}
Exceptions in async void methods are thrown in the current SynchronizationContext (or in the ThreadPool if the SynchronizationContext.Current is null), which normally results to the crashing of the process (source code).
Next, the YoloAsync method is not marked with the async modifier.
static Task YoloAsync()
{
throw new Exception();
}
Semantically it is an asynchronous method since it returns a Task, but the task is not generated from an async-state-machine. So the code inside the YoloAsync method is executed synchronously like any other method, and unlike the async-state-machine-generated methods that propagate their exceptions through the Task they return.
Maxim 26. "Fire and Forget" is fine, provided you never actually forget.
Getting a Exception in Multitasking is always difficulty.
If you are doing Mutlthreading, it is actually trivially easy to loose all exceptions by default. The thread itself swallows all Exceptions. This is the worst case that has to be handeled.
As a result, Multitasking approaches always catch all exceptions, then expose them in the Result. Task has a property for that. So Does RunWorkerCompletedEventArgs.
One job of the continuation code, is to check for and re-raise any Exceptions. It is something Task has to do for Multithreading support, even if it does not nessearily make sense with mere Multitasking.
In constructor I want to call one method type :
private async Task OnLoadPrometDanKorisnikDatum
and I want to wait that method while its finish, and I have more method(3) like this and I want to call this 3 methods in background thread and don't wait him to finish, just want to wait first method. And I want to them executing parallel.
I have methods async Task,and in constructor of view model I call like this
OnLoadPrometDanKorisnikDatum(KorisnikID, PomocnaDnDDatnaDat,
DatumVrednost).Wait();
OnLoadPrometNedelja(KorisnikID, PomocnaDnDDatnaDatNedelja).Wait();
if I don't place .Wait() on the end, program doesn't work. I see in debug mode they run asynchronly, but time spent tell me that they sub(one method time + second method time + ....).
Can someone help me, this is for me very stuf...
Answer
The best way to handle your scenario is to use async void.
I recommend first reading the Explanation section below to fully understand the best practices around async void.
public MyConstructor()
{
ExecuteAsyncMethods();
}
async void ExecuteAsyncMethods()
{
try
{
await OnLoadPrometDanKorisnikDatum(KorisnikID, PomocnaDnDDatnaDat, DatumVrednost);
await OnLoadPrometNedelja(KorisnikID, PomocnaDnDDatnaDatNedelja);
}
catch(Exception e)
{
//Handle Exception
}
}
Explanation
Many C# devs are taught "Never use async void", but this is one of the few use-cases for it.
Yes async void can be dangerous and here's why:
Cannot await an async avoid method
Can lead to race conditions
Difficult to catch an Exception thrown by async void methods
E.g. the following try/catch block will not catch the Exception thrown here:
public MyConstructor()
{
try
{
//Cannot await `async void`
AsyncVoidMethodWithException();
}
catch(Exception e)
{
//Will never catch the `Exception` thrown in `AsyncVoidMethodWithException` because `AsyncVoidMethodWithException` cannot be awaited
}
//code here will be executing by the time `AsyncVoidMethodWithException` throws the exception
}
async void AsyncVoidMethodWithException()
{
await Task.Delay(2000);
throw new Exception();
}
That being said, as long as we wrap the contents of our entire async void in a try/catch block, we will be able to catch the exception, like so:
public MyConstructor()
{
AsyncVoidMethodWithException();
}
async void AsyncVoidMethodWithException()
{
try
{
await Task.Delay(2000);
throw new Exception();
}
catch(Exception e)
{
//Exception will be caught and successfully handled
}
}
SafeFireAndForget
I created a library to help with this and its additional benefit is that it avoids writing async void code that could be potentially misused by future devs.
It's open source and also available on NuGet:
Source Code
NuGet Package
SafeFireAndForget
SafeFireAndForget allows us to safely execute a Task whilst not blocking the calling thread and without waiting for it to finish before moving to the next line of code.
Below is a simplified version of SafeFireAndForget that you can add to your project.
However, I recommend copy/pasting its complete source code or adding its NuGet Package to your library to get a more robust implementation
public static async void SafeFireAndForget<TException>(this Task task, Action<TException> onException = null, bool continueOnCapturedContext = false) where TException : Exception
{
try
{
await task.ConfigureAwait(continueOnCapturedContext);
}
catch (TException ex) when (onException != null)
{
onException(ex);
}
}
Using SafeFireAndForget
To use SafeFireAndForget, append it to your method call like so:
OnLoadPrometDanKorisnikDatum(KorisnikID, PomocnaDnDDatnaDat, DatumVrednost).SafeFireAndForget();
OnLoadPrometNedelja(KorisnikID, PomocnaDnDDatnaDatNedelja).SafeFireAndForget();
To handle any Exception thrown by that Task, use onException. Here's an example that prints the Exception to the Debug Console:
OnLoadPrometDanKorisnikDatum(KorisnikID, PomocnaDnDDatnaDat, DatumVrednost).SafeFireAndForget(ex => Debug.WriteLine(ex));
OnLoadPrometNedelja(KorisnikID, PomocnaDnDDatnaDatNedelja).SafeFireAndForget(ex => Debug.WriteLine(ex));
I have this code:
//note that this Action is a very time consuming process so I need to wrap it in Task.Run
private static async Task RunBeforeCompletion(Action action)
{
var task = Task.Run(() =>
{
Console.WriteLine("start");
action(); //some exception could have happened here, or I could have used the Task incorrectly that results in exception
//how to catch this exception?
});
await task.ContinueWith(t =>
{
Console.WriteLine(t.Exception.Message);
ExceptionDispatchInfo.Capture(t.Exception.InnerException).Throw();
},
TaskContinuationOptions.OnlyOnFaulted);
}
private static void Sleep()
{
Console.WriteLine("sleep");
Thread.Sleep(1000);
}
private static void RunAll()
{
var tsk = RunBeforeCompletion(Sleep)
.ContinueWith(t1 =>
{
Console.WriteLine("run");
});
Task.WaitAll(tsk);
}
static void Main(string[] args)
{
RunAll();
}
Note that the Action is a very time consuming process so I need to wrap it in Task.Run
Which works fine. The code can run well without exception if there is no exception thrown from the Action
However, if I move the method body of RunBeforeCompletion to another method, then a TaskCanceledException will be thrown. ie: the following code will throw a TaskCanceledException.
private static async Task WrapTask(Action action)
{
var task = Task.Run(() =>
{
Console.WriteLine("start");
action(); //some exception could have happened here, or I could have used the Task incorrectly that results in exception
//how to catch this exception?
});
await task.ContinueWith(t =>
{
Console.WriteLine(t.Exception.Message);
ExceptionDispatchInfo.Capture(t.Exception.InnerException).Throw();
},
TaskContinuationOptions.OnlyOnFaulted);
}
private static async Task RunBeforeCompletion(Action action)
{
await WrapTask(action);
}
private static void Sleep()
{
Console.WriteLine("sleep");
Thread.Sleep(1000);
}
private static void RunAll()
{
var tsk = RunBeforeCompletion(Sleep)
.ContinueWith(t1 =>
{
Console.WriteLine("run");
});
Task.WaitAll(tsk);
}
static void Main(string[] args)
{
RunAll();
}
From what I understand, this is because TaskContinuationOptions.OnlyOnFaulted only works in a single task and not multi-task continuation.
Do note that the crash will only happen when I run the second case code in VS 2015 with debugger attached and with Exception Settings-> Break at all Exception.
If I run without a debugger, or if I don't require the VS to break when TaskCanceledException is thrown, then no problem. Whatever it is, TaskCanceledException should never be thrown.
First question:
But aren't the first and second method the same? I just move the original Task in a separate method.
I believe that I should always be using the TaskContinuationOptions.OnlyOnFaulted option on a single task only as per guideline, that's why I put it immediately after a Task.Run, so that I know that I don't unconsciously chain it with other ContinueWith statement right after a Task.Run.
And what does this mean? Why the TaskCanceledException is thrown with debugger attached but not thrown if no debugger? In that case, how can I be sure all the tasks are finishing successfully or not?
What purpose I'm trying to accomplish?
During action, some exceptions can be thrown, or I might use the Task channing incorrectly, so I want to log the exception first ( using Console.WriteLine as a stub in for this toy example), before I rethrow the exception up for further handling. This is because any exceptions thrown inside Task.Run ( or anything to do with Task) will be swallowed up and later will result in a very mysterious crash. So I want to log the exception.
So my second question is, given that if the user of WrapTask can chain the method up with other ContinueWith construct for as long as he wants, how to write the exception handling code ( for the purpose of logging the exception error) elegantly?
I'm trying to implement exception handling from a task but I just can't seem to get it right. I've found a few examples of the pattern but no matter what I try I always seem to get unhandled exceptions from within the task itself. I must be missing something, but I can't see what. Any help would be very much appreciated.
I've tried an example I found on MSDN but that doesn't work for me:
https://msdn.microsoft.com/en-us/library/dd997415(v=vs.110).aspx
And I followed the answer to this question but when I run the fix in Visual Studio it still complains about unhandled exceptions:
ContinueWith TaskContinuationOptions.OnlyOnFaulted does not seem to catch an exception thrown from a started task
This is the code that I initially wrote:
static void Main(string[] args)
{
TestAsync().ContinueWith(t =>
{
Console.WriteLine(t.Exception.ToString());
}, TaskContinuationOptions.OnlyOnFaulted);
Console.ReadKey();
}
static async Task<IEnumerable<string>> TestAsync()
{
IEnumerable<string> list = null;
try
{
list = await TestTask();
}
catch (Exception)
{
Console.WriteLine("Caught!");
}
return list;
}
static Task<IEnumerable<string>> TestTask()
{
var task = new Task<IEnumerable<string>>(() =>
{
throw new AggregateException("This is a test");
});
task.Start();
return task;
}
Just hit continue after VS breaks, you will see it gets to your ContinueWith. It is just a quirk of the debugger because it cannot find a try/catch within your code that handles the execption.
If you don't want the debugger to stop and show you a message you will need to disable "Just My Code" in the debugger options so that the try/catch that lives inside of Task gets counted as the thing that catches the exception.
I am trying to convert an application to use Tasks instead of Microsoft's multithreaded framework, but I'm having trouble with the error handling. From Microsoft's documentation ( http://msdn.microsoft.com/en-us/library/vstudio/0yd65esw.aspx ), I would expect the try-catch below to catch the exception:
private async void Button1_Click()
{
try
{
object obj = await TaskFunctionAsync()
}
catch(Exception ex)
{}
}
public Task<object> TaskFunctionAsync()
{
return Task.Run<object>(() =>
{
throw new Exception("foo");
return new object();
});
}
but when Button1_Click is fired, I get an unhandled exception within the lambda expression. Is there some way to get the exception out into the try-catch? I thought that this kind of error handling (so you don't need to marshal from the task worker thread) was one of the major benefits of the Task framework.
I've also tried:
public async Task<object> TaskFunctionAsync()
{
return await Task.Run<object>(() =>
{
throw new Exception("foo");
return new object();
});
}
but when Button1_Click is fired, I get an unhandled exception within
the lambda expression
That's not true. It is unhandled by user-code because the framework catches it, but not completely unhandled. Continue running the application to see that the exception will be caught by the catch in Button1_Click.