10

我正在阅读有关async/await关键字的内容,并且已经阅读过:

当逻辑流到达等待令牌时,调用线程将暂停,直到调用完成。

好吧,我创建了一个简单的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";
            });            
        }

我不明白的是,当我单击按钮时,label1 会有文本Running,而标签只有在 10 秒后才会有相同的文本,但是在这 10 秒内我能够在我的文本框中输入文本,所以似乎主线程正在运行...

那么,异步/等待是如何工作的呢?

这是书中的“截图”: 在此处输入图像描述

问候

4

4 回答 4

22

我读过:当逻辑流到达等待令牌时,调用线程将暂停,直到调用完成。

你从哪里读到这些废话?要么有一些你没有引用的上下文,要么你应该停止阅读包含这个的任何文本。await 的目的是做相反的事情。await 的目的是在异步任务运行时保持当前线程做有用的工作

更新:我下载了你引用的书。该部分中的所有内容绝对是错误的。把这本书扔掉,买一本更好的书。

我不明白的是,当我单击按钮时,label1 将显示文本正在运行,并且标签仅在 10 秒后才会具有相同的文本,但在这 10 秒内,我能够在文本框中输入文本,所以似乎主线程正在运行......

这是正确的。这是发生的事情:

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

文本已设置。

        button1.Text =  await DoWork();

一堆事情发生在这里。首先会发生什么? DoWork叫做。它有什么作用?

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

它从线程池中抓取一个线程,使该线程休眠十秒钟,然后返回一个表示该线程正在完成的“工作”的任务。

现在我们回到这里:

        button1.Text =  await DoWork();

我们手头有一个任务。await 首先检查任务是否已经完成。它不是。接下来,它会将此方法的其余部分作为任务的延续。然后它返回给它的调用者。

嘿,它的调用者是什么?反正我们是怎么到这里的?

一些代码称为此事件处理程序;它是处理 Windows 消息的事件循环。它看到一个按钮被单击并分派给刚刚返回的单击处理程序。

现在会发生什么?事件循环继续运行。正如您所注意到的,您的 UI 一直运行良好。最终,该线程在 10 秒内结束,任务的继续被激活。那有什么作用?

这会在 Windows 队列中发布一条消息,说“您现在需要运行该事件处理程序的其余部分;我有您正在寻找的结果。”

主线程事件循环最终会得到该消息。所以事件处理程序从它停止的地方开始:

        button1.Text =  await DoWork();

await 现在从任务中提取结果,将其存储在按钮文本中,然后返回到事件循环。

于 2016-06-06T22:56:53.683 回答
0

async/await创建一个为您处理延续的状态机。一个非常粗略的等价物(没有一堆特征)是一个显式的延续方法,例如:

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");
    });
}

请注意,我仍然使用Task.Run在单独的线程上运行。请注意,此版本无法跟踪进度(而原始版本可以更改 to 的返回值button1_ClickTask查看它是否已完成)。

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

于 2016-06-06T22:15:18.783 回答
0

通过写作await,您是在说 - 请在此时停止执行该方法,等待DoWork完成,然后才能继续。

What Happens in an Async Method部分中使用 async 和 await (C#) 进行的异步编程对方法中发生的事情进行了分步说明,并附有图片async

一个更好的解释是在await (C# reference)。看看下面WaitSynchronously的评论WaitAsynchronouslyAsync

文章还指出(强调我的):

await 运算符应用于异步方法中的任务,以暂停该方法的执行,直到等待的任务完成。任务代表正在进行的工作。

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";
}
于 2016-06-06T22:33:20.317 回答
0

基本上,关键是程序以同步方式继续执行,直到或除非遇到等待。一旦遇到等待,假设对于任务 A,编译器将切换回调用此异步方法的 main 方法,而没有 await 关键字,并将继续从任务 A 的调用点执行程序,因为编译器知道它必须等待任务 A 的完成,为什么不完成它的其他待处理任务。

这里发生的是在 main 方法中没有等待 button1_Click 事件,因此一旦遇到 await DoWork() 它将继续执行。DoWork() 完成后,编译器将继续执行 button1_Click 中的进一步代码

于 2020-12-21T11:53:44.727 回答