简体   繁体   English

异步等待:主线程是否挂起?

[英]async await: is the main thread suspended?

I was reading about async/await keywords and I've read that:我正在阅读有关async/await关键字的内容,并且已阅读:

When the flow of logic reaches the await token, the calling thread is suspended until the call completes.当逻辑流到达 await 标记时,调用线程将挂起,直到调用完成。

Well, I've created a simple windows forms application , placed two labels, a button and a textbox and I wrote the code:好吧,我创建了一个简单的windows forms application ,放置了两个标签、一个按钮和一个文本框,然后我编写了代码:

        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...我不明白的是,当我单击按钮时,label1 将显示正在Running的文本,并且标签将在 10 秒后显示相同的文本,但是在这 10 秒内,我能够在我的文本框中输入文本,所以看起来主线程正在运行......

So, how does the async/await works?那么,async/await 是如何工作的呢?

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. await 的目的是做相反的事情。 The point of await is to keep the current thread doing useful work while the asynchronous task is in flight . await 的目的是让当前线程在异步任务进行中时做有用的工作

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...我不明白的是,当我单击按钮时,标签 1 将显示正在运行的文本,标签将在 10 秒后显示相同的文本,但在这 10 秒内,我能够在文本框中输入文本,所以看起来主线程正在运行......

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. DoWork被调用。 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.它从线程池中取出一个线程,让该线程休眠 10 秒,然后返回一个代表该线程正在完成的“工作”的任务。

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. Await 首先检查任务是否已经完成。 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.它是处理 Windows 消息的事件循环。 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.正如您所注意到的,您的 UI 一直运行良好。 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."这会在 Windows 队列中发布一条消息,说“您现在需要运行该事件处理程序的其余部分;我得到了您正在寻找的结果。”

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. await 现在从任务中提取结果,将其存储在按钮文本中,然后返回到事件循环。

async / await creates a state machines that handles the continuation for you. async / await创建一个状态机,为您处理延续。 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.请注意,我仍在使用Task.Run在单独的线程上运行。 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).注意这个版本是没​​有进度跟踪的(原来可以把button1_Click的返回值改成Task看是否完成)。

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.除了上述转换之外,系统还智能判断Task是否在 UI 线程上启动并再次编组回 UI 线程以确保一切按预期进行。 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).这可能是您没有意识到的主要事情,但在状态机方面的扩展有时会解释asyc / await真正含义(而不是让它变得神秘)。

By writing await , you are saying - please stop execution of the method at this point, wait for DoWork to finish and only then continue.通过编写await ,您是在说 - 此时请停止执行该方法,等待DoWork完成,然后再继续。

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. 与异步并等待(C#),异步编程在异步方法部分会发生什么有一个一步一步的解释,与图片的在发生什么, async方法。

An even better explanation is at await (C# reference) .更好的解释是await (C# 参考) Have a look at comments for WaitSynchronously and WaitAsynchronouslyAsync below.看看下面对WaitSynchronouslyWaitAsynchronouslyAsync评论。

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. await 运算符应用于异步方法中的任务,以暂停该方法的执行直到等待的任务完成。 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.基本上,关键是程序执行以同步方式继续,直到或除非遇到 await。 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.一旦遇到 await,假设对于任务 A,编译器将切换回调用此异步方法的 main 方法,而无需使用 await 关键字,并将继续从任务 A 的调用点执行程序,因为编译器知道它必须等待完成任务 A 那么为什么不完成它的其他待处理任务。

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.这里发生的事情是 main 方法中不等待 button1_Click 事件,因此一旦遇到 await DoWork() 它将继续执行。 And after DoWork() is completed compliler will continue to execute the further code in button1_Click DoWork() 完成后,编译器将继续执行 button1_Click 中的进一步代码

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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