What are the differences between `async void` (with no await) vs `void` - c#

Taken from article on async await by Stephen Cleary:
Figure 2 Exceptions from an Async Void Method Can’t Be Caught with Catch
private async void ThrowExceptionAsync()
{
  throw new InvalidOperationException();
}
public void AsyncVoidExceptions_CannotBeCaughtByCatch()
{
  try
  {
    ThrowExceptionAsync();
  }
  catch (Exception)
  {
    // The exception is never caught here!
    throw;
  }
}
... any exceptions thrown out of an async void method will be raised directly on the SynchronizationContext that was active when the async void method started...
What does that actually mean? I wrote an extended example to try and glean more info. It has the same behaviour as Figure 2:
static void Main()
{
AppDomain.CurrentDomain.UnhandledException += (sender, ex) =>
{
LogCurrentSynchronizationContext("AppDomain.CurrentDomain.UnhandledException");
LogException("AppDomain.CurrentDomain.UnhandledException", ex.ExceptionObject as Exception);
};
try
{
try
{
void ThrowExceptionVoid() => throw new Exception("ThrowExceptionVoid");
ThrowExceptionVoid();
}
catch (Exception ex)
{
LogException("AsyncMain - Catch - ThrowExceptionVoid", ex);
}
try
{
// CS1998 C# This async method lacks 'await' operators and will run synchronously.
async void ThrowExceptionAsyncVoid() => throw new Exception("ThrowExceptionAsyncVoid");
ThrowExceptionAsyncVoid();
}
// exception cannot be caught, despite the code running synchronously.
catch (Exception ex)
{
LogException("AsyncMain - Catch - ThrowExceptionAsyncVoid", ex);
}
}
catch (Exception ex)
{
LogException("Main", ex);
}
Console.ReadKey();
}
private static void LogCurrentSynchronizationContext(string prefix)
=> Debug.WriteLine($"{prefix} - " +
$"CurrentSynchronizationContext: {SynchronizationContext.Current?.GetType().Name} " +
$"- {SynchronizationContext.Current?.GetHashCode()}");
private static void LogException(string prefix, Exception ex)
=> Debug.WriteLine($"{prefix} - Exception - {ex.Message}");
Debug output:
Exception thrown: 'System.Exception' in ConsoleApp3.dll
AsyncMain - Catch - ThrowExceptionVoid - Exception - ThrowExceptionVoid
Exception thrown: 'System.Exception' in ConsoleApp3.dll
An exception of type 'System.Exception' occurred in ConsoleApp3.dll but was not handled in user code
ThrowExceptionAsyncVoid
AppDomain.CurrentDomain.UnhandledException - CurrentSynchronizationContext: -
AppDomain.CurrentDomain.UnhandledException - Exception - ThrowExceptionAsyncVoid
The thread 0x1c70 has exited with code 0 (0x0).
An unhandled exception of type 'System.Exception' occurred in System.Private.CoreLib.ni.dll
ThrowExceptionAsyncVoid
The program '[18584] dotnet.exe' has exited with code 0 (0x0).
I want more details
If there is no current synchronization context (as in my example), where is the exception raised?
What are the differences between async void (with no await) and void
The compiler warns CS1998 C# This async method lacks 'await' operators and will run synchronously.
If it runs synchronously with no await, why is it behaving differently from simply void?
Does async Task with no await also behave differently from Task?
What are the differences in compiler behaviour between async void and async Task. Is a Task object really created under-the-hood for async void as suggested here?
Edit. To be clear, this isn't a question about best practices - it is a question about compiler / runtime implementation.

If there is no current synchronization context (as in my example), where is the exception raised?
By convention, when SynchronizationContext.Current is null, that's really the same as SynchronizationContext.Current equal to an instance of new SynchronizationContext(). In other words, "no synchronization context" is the same as the "thread pool synchronization context".
So, the behavior you're seeing is that the async state machine is catching the exception and then raising it directly on a thread pool thread, where it cannot be caught with catch.
This behavior seems odd, but think about it this way: async void is intended for event handlers. So consider a UI application raising an event; if it is synchronous, then any exceptions get propagated to the UI message processing loop. The async void behavior is intended to mimic that: any exceptions (including ones after await) are re-raised on the UI message processing loop. This same logic is applied to the thread pool context; e.g., exceptions from your synchronous System.Threading.Timer callback handler will be raised directly on the thread pool, and so will exceptions from your asynchronous System.Threading.Timer callback handler.
If it runs synchronously with no await, why is it behaving differently from simply void?
The async state machine is handling the exceptions specially.
Does async Task with no await also behave differently from Task?
Absolutely. async Task has a very similar state machine - it catches any exceptions from your code and places them on the returned Task. This is one of the pitfalls in eliding async/await for non-trivial code.
What are the differences in compiler behaviour between async void and async Task.
For the compiler, the difference is just how exceptions are handled.
The proper way to think about this is that async Task is a natural and appropriate language development; async void is a weird hack that the C#/VB team adopted to enable asynchronous events without huge backwards-compatibility issues. Other async/await-enabled languages such as F#, Python, and JavaScript have no concept of async void... and thus avoid all of its pitfalls.
Is a Task object really created under-the-hood for async void as suggested here?
No.

async void - It can't be awaited and it allows you to fire or forget methods
async Task - It can be awaited, but does not return any value
async Task methodName { return default(T); } - It can be awaited, and returns a value of the type T
void - no argument will be returned

Related

Exception thrown in async method is not caught - why?

I have a hard time understanding how async/await works in various non-happy-path cases. For example, I have the following code:
class Program
{
static void Main(string[] args)
{
Do();
Console.ReadLine();
}
private static void Do()
{
TaskScheduler.UnobservedTaskException += (s, e) =>
{
Console.WriteLine($"Unobserved Exception : {e.Exception.Message}");
e.SetObserved();
};
try
{
ThrowsAsync();
}
catch (Exception ex)
{
Console.WriteLine($"Caught in try/catch : {ex.Message}");
}
}
private static async Task ThrowsAsync()
{
Console.WriteLine("Throwing");
throw new Exception("FAILURE");
}
}
There are two things that I do not understand:
The ThrowsAsync method is async, however, it does not contain any await. I would assume that in such a case the method would execute like a "normal" synchronous method. However, the exception that it throws is never caught in the catch block.
Trying to somehow catch the exception, I added the handler for TaskScheduler.UnobservedTaskException. However, it is never executed. Why is that?
I know that the exception would be caught if I awaited ThrowsAsync. However, I'm experimenting to get a better understanding of how it works.
I'm running that code using .NET 5 and Linux-based OS.
As described for example in this blog post by Stephen Cleary - the state machine for async methods will capture exceptions from your code and place them on the returned task, i.e. method invocation will not throw, you will be able to catch exception if await the result.
As for TaskScheduler.UnobservedTaskException - check out this answer and be sure to run code in Release mode.

Exception in async methods is not caught

The following code does not catch my OperationCancelException which is thrown by calling ct.ThrowIfCancellationRequested.
public partial class TitleWindow : Window, IAsyncInitialization
{
public Task Initialization{get; private set;}
CancellationTokenSource cts;
public TitleWindow()
{
InitializeComponent();
cts = new CancellationTokenSource();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
try
{
cts.Cancel();
Initialization = GetCancelExceptionAsync(cts.Token);
}
catch (OperationCanceledException)
{
Console.WriteLine("Operation canceled!");
}
}
public async Task GetCancelExceptionAsync(CancellationToken ct)
{
await Task.Delay(1000);
ct.ThrowIfCancellationRequested();
}
}
However if i replace my Window_Loaded method with the following (making it async and await the call of my async method), the exception gets caught.
private async void Window_Loaded(object sender, RoutedEventArgs e)
{
try
{
cts.Cancel();
await GetCancelExceptionAsync(cts.Token);
}
catch (OperationCanceledException)
{
Console.WriteLine("Operation canceled!");
}
}
Why is my first approach not working? Is the exception not properly propagated to the correct synchronization context?
I was trying to use The Asynchronous Initialization Pattern described in Stephen Clearys blog post to be able to later on await a task which was started in a constructor (and in order to make it comparable to my second example I used the (async) Window_Loaded event to await methods there right away, like suggested to me in a previous question). Then I wanted to provide an option to cancel the async method that I started in the constructor, where i am currently stuck because the exception handling does not work as I expected.
With my "non-working" code, I can catch the exception by putting await Initialization in a try-catch block somewhere, but I still get an additional unhandled exception.
How do I implement this in a way that allows me to await my async method later on (to ensure that I do not work with an inconsistent state of my object) and still being able to cancel that long-running Task (which would of course need to return/set default values)?
In your first example the exception is not caught because it does not occure before leaving the try/catch block. If you want to catch it there you need to wait/await it there exactly like you do in the second example.
If you do not await the returned task the method continues execution and leaves the try/catch block before the exception actually occures...
If you want to catch the exception "out of band" you can also register to TaskScheduler.UnobservedTaskException (this event is called if a task is throwing an exception which is nowhere caught) to get all uncaught exceptions or monitor the tasks Exception property. May also check out THIS answer.
Exeption is thrown in the task on another thread.
public async Task GetCancelExceptionAsync(CancellationToken ct)
{
try
{
await Task.Delay(1000);
ct.ThrowIfCancellationRequested();
}
catch (Exception e)
{
// your Cancleation expeption
}
}

Async / Await Lambdas

I have a strange problem combining the async/await to make it work:
I created a small procedure, which should handle basically the try/catch of every action:
internal static void HandledAction(Action action, Info infoBar)
{
try
{
action();
}
catch (Exception ex)
{
infoBar.SetError("An Exception occured: " + ex.Message);
WriteLog(ex.StackTrace);
}
Nohing to fancy, but it's worth since changing error-handling is very easy made.
But what happens, if I'd like to get Data async in the Lambda? Lets take this simple example:
private void mnuImportData_Click(object sender, RoutedEventArgs e)
{
ActionHelper.HandledAction(async () =>
{
throw new NotImplementedException("Ups");
}, infoMain);
}
Sure, the HandledAction gets called, passes, since it gets the pointer back, and the exception gets thrown, of course not handled.
I imagine I have to create a AsyncHandledAction, and set the action async, but is there a easier way to solve this problem?
I guess many people use a central exception-handling, and there are far better solutions for this?
Thanks in advance
Matthias
Edit: I created an example, which should shpw netter I need: I basically dont want the whole Action I pass being awaitable, but one call in the Lambda is:
ActionHelper.HandledActionAsync(() =>
{
//elided
CheckFileResult rslt = await excelImport.CheckFilesAsync(tmpPath);
//elided
}, infoMain);
Of course, by doing so, I get the error:
Error 3 The 'await' operator can only be used within an async lambda expression. Consider marking this lambda expression with the 'async' modifier.
The reason is: Action instead of Func. Since yours:
async () =>
{
throw new NotImplementedException("Ups");
}
in fact is:
async void Method() { }
when Func<Task> is:
async Task Method() { }
Async void will capture SynchronizationContext.Current and when exception is thrown it will be posted to SynchronizationContext by SynchronizationContext.Post() - (in windows runtime you can catch these types of exception). In case of ASP.NET/Console application SynchronizationContext.Current returns null which means that exception would be propagated to Thread Pool and I'm not sure but I think it is not possible to catch it. However when there is returned Task by asynchronous method exception could be marshaled to the caller through this returned Task. It is also worth mention that async lambda expresions will always prefer methods with Func<Task> over Action. General rule is: never use async void (async Action) unless it is "top-level method" (in example event handler).
You need an async version of HandleAction
internal static async Task HandledAction(Func<Task> action, Info infoBar)
{
try
{
await action();
}
catch (Exception ex)
{
infoBar.SetError("An Exception occured: " + ex.Message);
WriteLog(ex.StackTrace);
}
}
of course you should call the method with await
private async void mnuImportData_Click(object sender, RoutedEventArgs e)
{
await ActionHelper.HandledAction(async () =>
{
throw new NotImplementedException("Ups");
}, infoMain);
}

Unobserved Task exceptions in .NET 4.5 still crash app

In Steven Toub's article:
To make it easier for developers to write asynchronous code based on
Tasks, .NET 4.5 changes the default exception behavior for unobserved
exceptions. While unobserved exceptions will still cause the
UnobservedTaskException event to be raised (not doing so would be a
breaking change), the process will not crash by default. Rather, the
exception will end up getting eaten after the event is raised,
regardless of whether an event handler observes the exception.
But the result of my experiment does not match the above statement. Below is my code:
static void Main(string[] args)
{
DownloadAsync("http://an.invalid.url.com);
}
async static void DownloadAsync(string url)
{
using (var client = new System.Net.Http.HttpClient())
{
string text = await client.GetStringAsync(url);
Console.WriteLine("Downloaded {0} chars", text.Length);
}
}
Since I pass an invalid url to DownloadAsync() method, the call to HttpClient's GetStringAsync() method will throw an expcetion, and it crashes the application.
So my question is: Does unobserved exceptions in .NET 4.5 still crash app by default?
You do have a Task with an exception (the one returned by GetStringAsync). However, the await is observing the Task exception, which then propagates out of the DownloadAsync method (which is async void).
Exceptions propagating out of async void methods behave differently; they are raised on the SynchronizationContext that was active when the async void method started (in this case, a thread pool SynchronizationContext). This is not considered an unobserved exception.
If you change DownloadAsync to return Task, then you will have an actual unobserved Task exception, which will be ignored (correctly):
static void Main(string[] args)
{
DownloadAsync("http://an.invalid.url.com);
Console.ReadKey();
}
async static Task DownloadAsync(string url)
{
using (var client = new System.Net.Http.HttpClient())
{
string text = await client.GetStringAsync(url);
Console.WriteLine("Downloaded {0} chars", text.Length);
}
}

async/await - when to return a Task vs void?

Under what scenarios would one want to use
public async Task AsyncMethod(int num)
instead of
public async void AsyncMethod(int num)
The only scenario that I can think of is if you need the task to be able to track its progress.
Additionally, in the following method, are the async and await keywords unnecessary?
public static async void AsyncMethod2(int num)
{
await Task.Factory.StartNew(() => Thread.Sleep(num));
}
Normally, you would want to return a Task. The main exception should be when you need to have a void return type (for events). If there's no reason to disallow having the caller await your task, why disallow it?
async methods that return void are special in another aspect: they represent top-level async operations, and have additional rules that come into play when your task returns an exception. The easiest way is to show the difference is with an example:
static async void f()
{
await h();
}
static async Task g()
{
await h();
}
static async Task h()
{
throw new NotImplementedException();
}
private void button1_Click(object sender, EventArgs e)
{
f();
}
private void button2_Click(object sender, EventArgs e)
{
g();
}
private void button3_Click(object sender, EventArgs e)
{
GC.Collect();
}
f's exception is always "observed". An exception that leaves a top-level asynchronous method is simply treated like any other unhandled exception. g's exception is never observed. When the garbage collector comes to clean up the task, it sees that the task resulted in an exception, and nobody handled the exception. When that happens, the TaskScheduler.UnobservedTaskException handler runs. You should never let this happen. To use your example,
public static async void AsyncMethod2(int num)
{
await Task.Factory.StartNew(() => Thread.Sleep(num));
}
Yes, use async and await here, they make sure your method still works correctly if an exception is thrown.
For more information see: https://learn.microsoft.com/en-us/archive/msdn-magazine/2013/march/async-await-best-practices-in-asynchronous-programming
I have come across this very useful article about async and void written by Jérôme Laban:
https://jaylee.org/archive/2012/07/08/c-sharp-async-tips-and-tricks-part-2-async-void.html
The bottom line is that an async+void can crash the system and usually should be used only on the UI side event handlers.
The reason behind this is the Synchronization Context used by the
AsyncVoidMethodBuilder, being none in this example. When there is no
ambient Synchronization Context, any exception that is unhandled by
the body of an async void method is rethrown on the ThreadPool. While
there is seemingly no other logical place where that kind of unhandled
exception could be thrown, the unfortunate effect is that the process
is being terminated, because unhandled exceptions on the ThreadPool
effectively terminate the process since .NET 2.0. You may intercept
all unhandled exception using the AppDomain.UnhandledException event,
but there is no way to recover the process from this event.
When writing UI event handlers, async void methods are somehow
painless because exceptions are treated the same way found in
non-async methods; they are thrown on the Dispatcher. There is a
possibility to recover from such exceptions, with is more than correct
for most cases. Outside of UI event handlers however, async void
methods are somehow dangerous to use and may not that easy to find.
The problem with calling async void is that
you don’t even get the task back. You have no way of knowing when the function’s task has completed. —— Crash course in async and await | The Old New Thing
Here are the three ways to call an async function:
async Task<T> SomethingAsync() { ... return t; }
async Task SomethingAsync() { ... }
async void SomethingAsync() { ... }
In all the cases, the function is transformed into a chain of tasks. The difference is what the function returns.
In the first case, the function returns a task that eventually produces the t.
In the second case, the function returns a task which has no product, but you can
still await on it to know when it has run to completion.
The third case is the nasty one. The third case is like the second case, except
that you don't even get the task back. You have no way of knowing when
the function's task has completed.
The async void case is a "fire and
forget": You start the task chain, but you don't care about when it's
finished. When the function returns, all you know is that everything
up to the first await has executed. Everything after the first await
will run at some unspecified point in the future that you have no
access to.
I think you can use async void for kicking off background operations as well, so long as you're careful to catch exceptions. Thoughts?
class Program {
static bool isFinished = false;
static void Main(string[] args) {
// Kick off the background operation and don't care about when it completes
BackgroundWork();
Console.WriteLine("Press enter when you're ready to stop the background operation.");
Console.ReadLine();
isFinished = true;
}
// Using async void to kickoff a background operation that nobody wants to be notified about when it completes.
static async void BackgroundWork() {
// It's important to catch exceptions so we don't crash the appliation.
try {
// This operation will end after ten interations or when the app closes. Whichever happens first.
for (var count = 1; count <= 10 && !isFinished; count++) {
await Task.Delay(1000);
Console.WriteLine($"{count} seconds of work elapsed.");
}
Console.WriteLine("Background operation came to an end.");
} catch (Exception x) {
Console.WriteLine("Caught exception:");
Console.WriteLine(x.ToString());
}
}
}
A brief explanation:
async Task<T> method() await can be used to wait till the execution is completed and it will return value of type T
async Task method() await can be used to wait till the execution is completed but no data is returned
async void method() can't be awaited and no data is returned [Example: async event execution]
My answer is simple
you can not await void method
Error CS4008 Cannot await 'void' TestAsync e:\test\TestAsync\TestAsyncProgram.cs
So if the method is async, it is better to be awaitable, because you can lose the advantage ofasync.

Categories

Resources