async await:主线程被挂起了吗?

时间:2016-06-06 21:58:39

标签: c# async-await thread-state

我正在阅读有关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秒钟内我就是能够在我的文本框中输入文本,所以似乎主线程正在运行...

那么,async / await是如何工作的?

这是一个&#34;截图&#34;从书中: enter image description here

此致

4 个答案:

答案 0 :(得分:16)

  

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

你在哪里读到这些废话?在那里有一些你没有引用的上下文,或者你应该停止阅读包含它的任何文本。等待的目的是做那个相反。等待点是以保持当前线程在异步任务处于运行状态时执行有用的工作

更新:我下载了你引用的书。绝对是该部分的一切都是错误的。扔掉这本书,买一本更好的书。

  

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

这是对的。这是发生的事情:

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

文字已设定。

        button1.Text =  await DoWork();

这里发生了一堆事情。先发生什么事?调用DoWork。它做了什么?

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

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

现在我们回到这里:

        button1.Text =  await DoWork();

我们手头有一项任务。 Await首先检查任务是否已完成。它不是。接下来,它将此方法的其余部分注册为任务的 continuation 。然后它返回其调用者。

嘿,它的来电是什么?我们怎么到这儿来的?

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

现在会发生什么?事件循环继续运行。正如您所注意到的,您的UI保持良好的运行状态。最终该线程退出10秒并激活任务的继续。那是做什么的?

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

主线程事件循环最终会到达该消息。所以事件处理程序会从中断处继续:

        button1.Text =  await DoWork();

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

答案 1 :(得分:0)

<svg xmlns="http://www.w3.org/2000/svg" id="svg-icons"> <symbol viewBox="0 0 24 24" id="unlocked"> <path d="M18,9h-1V7c0-2.8-2.2-5-5-5S7,4.2,7,7h1.9c0-1.7,1.4-3.1,3.1-3.1s3.1,1.4,3.1,3.1v2H6c-1.1,0-2,0.9-2,2v9c0,1.1,0.9,2,2,2 h12c1.1,0,2-0.9,2-2v-9C20,9.9,19.1,9,18,9z"></path> </symbol> <symbol viewBox="0 0 24 24" id="locked"> <path d="M18,9h-1V7c0-2.8-2.2-5-5-5S7,4.2,7,7v2H6c-1.1,0-2,0.9-2,2v9c0,1.1,0.9,2,2,2h12c1.1,0,2-0.9,2-2v-9C20,9.9,19.1,9,18,9z M15.1,9H8.9V7c0-1.7,1.4-3.1,3.1-3.1s3.1,1.4,3.1,3.1V9z"></path> </symbol> </svg> <div> <a href="#" id="editIcon1">this is test 1 <svg class="icon icon-locked"> <use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#unlocked"> </use> </svg> </a> </div> <div> <a href="#" id="editIcon2">this is test 2 <svg class="icon icon-locked"> <use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#unlocked"></use> </svg> </a> </div> / 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的返回值更改为button1_Click以查看其是否已完成)。

除了上述转换之外,系统还明智地知道是否在UI线程上启动了Task并再次回到UI线程以确保一切正常运行。这可能是你没有意识到的主要事情,但扩展状态机方面有时会解释Task / asyc的真正含义(而不是让它成为神秘的)。

答案 2 :(得分:0)

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

Asynchronous Programming with async and await (C#)异步方法中发生的事情部分详细解释了async方法中发生的事情。

更好的解释是await (C# reference)。请查看以下WaitSynchronouslyWaitAsynchronouslyAsync的评论。

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

  

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

答案 3 :(得分:0)

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

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