C# 6.0 become more asynchronous-friendly than before. Finally, you can use await keyword in catch and finally blocks! Here is example:
class Program
{
static void Main(string[] args)
{
do
{
Console.WriteLine("Before caller " + Thread.CurrentThread.ManagedThreadId);
CallerMethod();
Console.WriteLine("After caller " + Thread.CurrentThread.ManagedThreadId);
} while (Console.ReadKey().Key != ConsoleKey.Q);
}
public static async void CallerMethod()
{
try
{
throw new Exception();
}
catch (Exception)
{
Console.WriteLine("Before catch await " + Thread.CurrentThread.ManagedThreadId);
await TestAsync();
Console.WriteLine("After catch await " + Thread.CurrentThread.ManagedThreadId);
}
finally
{
Console.WriteLine("Before finally await " + Thread.CurrentThread.ManagedThreadId);
await TestAsync();
Console.WriteLine("After finally await " + Thread.CurrentThread.ManagedThreadId);
}
}
public static Task TestAsync()
{
return Task.Factory.StartNew(() =>
{
Console.WriteLine("Async method before sleep " + Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(1000);
Console.WriteLine("Async method after sleep " + Thread.CurrentThread.ManagedThreadId);
});
}
}
Here is console output:
Before caller 1
Before catch await 1
Async method before sleep 3
After caller 1
Async method after sleep 3
After catch await 3
Before finally await 3
Async method before sleep 4
Async method after sleep 4
After finally await 4
Everything as we expected. Here is visualization of the threads:
Internal Implementation
Generated IL code is pretty big for such small example, so I will not publish a whole code here. You can check it out on pastebin
C# compiler will create two nested classes for this example. One will contain "awaiting" logic and other just a wrapper for this anonymous method:
Console.WriteLine("Async method before sleep " + Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(1000);
Console.WriteLine("Async method after sleep " + Thread.CurrentThread.ManagedThreadId);
If you look in the IL code to the "awaiting" class you will find that it implements IAsyncStateMachine interface and all logic is in the MoveNext method.
This method uses System.Runtime.CompilerServices.TaskAwaiter class to wait for the threads to complete. It does this based on an internal state (integer variable).
Also, each await is "wrapped" with try\catch block. And all logic of waiting for a task to complete is there. Our original try\catch\finally block become more simple, almost empty try\catch block (without finally) which initialize internal state:
.try
{
IL_0037: nop
IL_0038: newobj instance void [mscorlib]System.Exception::.ctor()
IL_003d: throw
} // end .try
catch [mscorlib]System.Exception
{
IL_003e: stloc.1
IL_003f: ldarg.0
IL_0040: ldloc.1
IL_0041: stfld object cs6await.Program/'<CallerMethod>d__1'::'<>s__3'
IL_0046: ldarg.0
IL_0047: ldc.i4.1
IL_0048: stfld int32 cs6await.Program/'<CallerMethod>d__1'::'<>s__4'
IL_004d: leave.s IL_004f
} // end handler
So, as you can see, await in catch\finally works in the same way as regular await construction. Nothing new where added to CIL to support this keyword in those blocks.