Random sequence of execution of statements when using async await

Viewed 195

I have a very simple program as shown below.

 public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            Task task = DemoCompletedAsync();

            MessageBox.Show("Method returned");            
            MessageBox.Show("Exiting .....");
        }

        static async Task<string> DemoCompletedAsync()
        {
            MessageBox.Show("Before first await");

            await Task.Run(() => GetID());

            MessageBox.Show("After first await");

            return "Demo";
        }

        public static int GetID()
        {
            Thread.Sleep(2000);
            return 1;
        }
    }

While running the program without debug mode the message boxes appear in correct sequence. i.e.
"Before first await"
"Method returned"
"Exiting ....."
"After first await"

But while debugging the statement get executed in random manner somehow switching to and forth between MainWindow() and DemoCompletedAsync
The message boxed appear in below manner:
"Before first await" --- from DemoCompletedAsync
"Method returned" --- from MainWWindow
"After first await" --- from DemoCompletedAsync
"Exiting ....." --- from MainWindow

As per theory, the use of async keyword in the method name tells the compiler that this method will be awaiting on an asynchronous operation using the await keyword and as soon as the await keyword is encountered, the control would pass on to the calling code. However, the asynchronous operation signified by the await keyword would continue executing. Once the asynchronous operation is over, rest of the method is executed. So why I am getting this strange behavior such as some statements get executed from MainWindow and some from DemoCompletedAsync switching to and forth from each other. I have also observed one more problem related to message boxes. MessageBox.Show is modal dialog box. The second one should not appear until the first closes. But in the case of given program, I actually get two message boxes simultaneously as the control switches between MainWindow and DemoCompletedAsync.

1 Answers

All these happened due to this line:

Task task = DemoCompletedAsync();

You didn't await this task, so when instruction pointer reached to real Thread (here Task.Run(() => GetID());) next lines executed immediately. So next line that executed could be one of MessageBox.Show("Method returned"); or MessageBox.Show("After first await"); randomely!

But the point here is that you can't await this task because you're in constructor and it can't be async. Also you can't wait this task like DemoCompletedAsync().Wait() because you're in Synchronization Context and a deadlock will occur.

So you should call it this way:

DemoCompletedAsync().ConfigureAwait(false).GetAwaiter().GetResult();

and use Dispatcher.Invoke in DemoCompletedAsync method for GUI actions

Related