How to dispose properly using async and await
Let's isolate each piece of code:
public static void ThreadDoWork()
{
using (var dispose = new ThreadDispose())
{
dispose.RunAsync();
}
}
public void RunAsync()
{
ThreadPool.QueueUserWorkItem(state =>
{
Thread.Sleep(3000);
});
}
What you do in this first piece of code is queue work on a threadpool thread. Because you're running this code inside a using
scope and it runs asynchronously on a different thread, it disposes immediately. That is why you see the dispose message inside your text file.
public static async void TaskDoWork()
{
using (var dispose = new TaskDispose())
{
await dispose.RunAsync();
}
}
public class TaskDispose : IDisposable
{
public async Task RunAsync()
{
await Task.Delay(3000);
}
}
When you await
inside your method, what you actually say is something along the lines of: "Execute this code. Because it's asynchronous by nature, I will return control back to the calling method, please call me back once you complete the asynchronous operation".
Your code hits the await
keyword and returns control to your Main
method. Inside Main
, your async method is the last piece of code to execute, hence finishing your application, and not giving a chance for your Dispose
method to execute.
If you want it to dispose, you'll have to change the return type from void
to Task
and explicitly Wait
:
public static async Task TaskDoWork()
{
using (var dispose = new TaskDispose())
{
await dispose.RunAsync();
}
}
And now:
static void Main(string[] args)
{
ThreadDoWork();
TaskDoWork().Wait();
}
Side Note:
There are a couple of guidelines that should be followed:
async void
is for compatability with event handlers, there are rarely occasions outside that scope where it should be used. Instead, useasync Task
.Methods doing asynchoronous operation using TAP (Task Asynchronous Pattern) should end with the
Async
postfix.TaskDoWork
should beTaskDoWorkAsync
.Using
Wait
on aTask
can cause deadlocks. In this particular case it doesn't because a console application doesn't have aSynchronizationContext
and uses the threadpools. The recommended approach is to go "async all the way" and useawait
.
There is great reading material inside the async-await tag wiki, make sure to check it out.