DEV Community

Garry Xiao
Garry Xiao

Posted on

C# "fire and forget" method

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);
            }
        }
Enter fullscreen mode Exit fullscreen mode

Run result:

Main starts at 21:12:02
Main ends at 21:12:05, 1
TestTask exception at 21:12:08, 7
Enter fullscreen mode Exit fullscreen mode

Conclusions:

  1. Starting sync code inside "async void" method will be executed on the same thread of the main thread. That's "Thread.Sleep(3000)" means.
  2. 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);
            }
        }
Enter fullscreen mode Exit fullscreen mode

Run result:

Main starts at 22:14:17
Main ends at 22:14:17, 1
TestTask exception at 22:14:20, 4
Enter fullscreen mode Exit fullscreen mode

Conclusions:

  1. It's real "fire and forget" from the call.
  2. 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.

FYI:
https://docs.microsoft.com/en-us/archive/msdn-magazine/2013/march/async-await-best-practices-in-asynchronous-programming

Top comments (0)