Option 1: async void
static void Main(string[] args)
{
try
{
Console.WriteLine("Main starts at " + DateTime.Now.ToLongTimeString());
TestTask();
Console.WriteLine("Main ends at " + DateTime.Now.ToLongTimeString() + ", " + Thread.CurrentThread.ManagedThreadId);
}
catch (Exception ex)
{
Console.WriteLine("ex: " + ex.Message);
}
Console.Read();
}
static async void TestTask()
{
try
{
Thread.Sleep(3000);
await Task.Delay(3000);
throw new InvalidOperationException();
}
catch
{
Console.WriteLine("TestTask exception at " + DateTime.Now.ToLongTimeString() + ", " + Thread.CurrentThread.ManagedThreadId);
}
}
Run result:
Main starts at 21:12:02
Main ends at 21:12:05, 1
TestTask exception at 21:12:08, 7
Conclusions:
- Starting sync code inside "async void" method will be executed on the same thread of the main thread. That's "Thread.Sleep(3000)" means.
- Exception inside "async void" method cannot be catched if happend begins with the first async call because they are in different context. So if TestTask is without try/catch, then the application will be failed and no way to track.
Option 2: Task.Run
static void Main(string[] args)
{
try
{
Console.WriteLine("Main starts at " + DateTime.Now.ToLongTimeString());
Task.Run(TestTask);
Console.WriteLine("Main ends at " + DateTime.Now.ToLongTimeString() + ", " + Thread.CurrentThread.ManagedThreadId);
}
catch (Exception ex)
{
Console.WriteLine("ex: " + ex.Message);
}
Console.Read();
}
static void TestTask()
{
try
{
Thread.Sleep(3000);
Task.Delay(3000);
throw new InvalidOperationException();
}
catch
{
Console.WriteLine("TestTask exception at " + DateTime.Now.ToLongTimeString() + ", " + Thread.CurrentThread.ManagedThreadId);
}
}
Run result:
Main starts at 22:14:17
Main ends at 22:14:17, 1
TestTask exception at 22:14:20, 4
Conclusions:
- It's real "fire and forget" from the call.
- Try/catch is also very important because the Main try/catch is impossible to catch the TestTask exception. But the TestTask failure does not affect the Main processing.
So always avoid Async Void and use Task.Run when you want "fire and forget". From its nature, take care of the exception handle and multiple threads scenario, please do not share any resources with the main thread.
Top comments (1)
I think you might misleading about Task mechanism.
You must await Task unless it might got cancelled.
Your sample works because you trick the application with Console.Read();
Remove Console.Read(); then the task will be cancelled.