简体   繁体   中英

async await: is the main thread suspended?

I was reading about async/await keywords and I've read that:

When the flow of logic reaches the await token, the calling thread is suspended until the call completes.

Well, I've created a simple windows forms application , placed two labels, a button and a textbox and I wrote the code:

        private async void button1_Click(object sender, EventArgs e)
        {
            label1.Text = Thread.CurrentThread.ThreadState.ToString();
            button1.Text =  await DoWork();
            label2.Text = Thread.CurrentThread.ThreadState.ToString();
        }

        private Task<string> DoWork()
        {
            return Task.Run(() => {
                Thread.Sleep(10000);
                return "done with work";
            });            
        }

What I don't understand is that when I click the button, the label1 will have the text Running and the label will have the same text only after 10 seconds, but in these 10 seconds I was able to enter the text in my textbox, so it seems that the main thread is running...

So, how does the async/await works?

here is a "screenshot" from the book: 在此处输入图片说明

Regards

I've read that: When the flow of logic reaches the await token, the calling thread is suspended until the call completes.

Where did you read that nonsense? Either there is some context there that you're not quoting, or you should stop reading whatever text it is that contained this. The point of await is to do the opposite of that. The point of await is to keep the current thread doing useful work while the asynchronous task is in flight .

UPDATE: I downloaded the book you referenced. Absolutely everything in that section is wrong. Throw this book away and buy a better book.

What I don't understand is that when I click the button, the label1 will have the text Running and the label will have the same text only after 10 seconds, but in these 10 seconds I was able to enter the text in my textbox, so it seems that the main thread is running...

That's correct. Here's what happens:

        label1.Text = Thread.CurrentThread.ThreadState.ToString();

The text is set.

        button1.Text =  await DoWork();

A bunch of stuff happens here. What happens first? DoWork is called. What does it do?

        return Task.Run(() => { Thread.Sleep(10000);

It grabs a thread out of the thread pool, puts that thread to sleep for ten seconds, and returns a task representing the "work" being done by that thread.

Now we are back here:

        button1.Text =  await DoWork();

We have a task in hand. Await first checks the task to see if it is already complete. It is not. Next it signs up the remainder of this method as the continuation of the task. Then it returns to its caller.

Hey, what is its caller? How did we get here anyways?

Some code called this event handler; it was the event loop that is processing Windows messages. It saw a button was clicked and dispatched to the click handler, which has just returned.

Now what happens? The event loop keeps running. Your UI keeps on running nicely, as you noticed. Eventually that thread ticks off ten seconds and the task's continuation is activated. What does that do?

That posts a message into the Windows queue saying "you need to run the rest of that event handler now; I have the result you were looking for."

The main thread event loop eventually gets to that message. So the event handler picks up where it left off:

        button1.Text =  await DoWork();

The await now extracts the result from the task, stores it in the button text, and returns back to the event loop.

async / await creates a state machines that handles the continuation for you. A very rough equivalent (without a bunch of features) is an explicit continuation method, for instance:

private void button1_Click_Continuation(string value)
{
    button1.Text = value;
    label2.Text = Thread.CurrentThread.ThreadState.ToString();
}

private void button1_Click(object sender, EventArgs e)
{
    label1.Text = Thread.CurrentThread.ThreadState.ToString();
    DoWork(button1_Click_Continuation);
}

private void DoWork(Action<string> continuation)
{
    Task.Run(() => {
        Thread.Sleep(10000);
        continuation("done with work");
    });
}

Note that I am still using Task.Run to run on a seperate thread. Note that this version has no way of tracking progress (while the original could change the return value of button1_Click to Task to see if it has completed or not).

In addition to the above transformation the system is smart about whether a Task started on the UI thread and marshalls back to the UI thread again to ensure everything works as you expect. This is probably the main thing you didn't realize but expanding on the state machine aspect sometimes explains what asyc / await really means (instead of leaving it as mystical).

By writing await , you are saying - please stop execution of the method at this point, wait for DoWork to finish and only then continue.

Asynchronous Programming with async and await (C#) in What Happens in an Async Method section has a step by step explanation, with a picture, of what happens in a async method.

An even better explanation is at await (C# reference) . Have a look at comments for WaitSynchronously and WaitAsynchronouslyAsync below.

The article also states (emphasis mine):

The await operator is applied to a task in an asynchronous method to suspend the execution of the method until the awaited task completes. The task represents ongoing work.

private async void button1_Click(object sender, EventArgs e)
{
    // Call the method that runs asynchronously.
    string result = await WaitAsynchronouslyAsync();

    // Call the method that runs synchronously.
    //string result = await WaitSynchronously ();

    // Display the result.
    textBox1.Text += result;
}

// The following method runs asynchronously. The UI thread is not
// blocked during the delay. You can move or resize the Form1 window 
// while Task.Delay is running.
public async Task<string> WaitAsynchronouslyAsync()
{
    await Task.Delay(10000);
    return "Finished";
}

// The following method runs synchronously, despite the use of async.
// You cannot move or resize the Form1 window while Thread.Sleep
// is running because the UI thread is blocked.
public async Task<string> WaitSynchronously()
{
    // Add a using directive for System.Threading.
    Thread.Sleep(10000);
    return "Finished";
}

Basically the crux is the program execution continues in synchronous manner until or unless await is encountered. once await is encountered, suppose for a Task A, the compiler will switch back to the main method which called this asynchronous method without the await keyword and will continue the program executing from calling point of Task A as compiler knows it has to wait for the completion of Task A so why not to complete its other pending task.

Here what is happening is button1_Click event is not awaited in main method so it will continue to execute once await DoWork() is encountered. And after DoWork() is completed compliler will continue to execute the further code in button1_Click

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM