解释async再次等待

时间:2014-08-01 10:53:09

标签: c# .net async-await

这是我的事件处理程序代码:

protected async void TestrunSaveExecute()
{
    bool saveResult = await SaveTestRunAsync();
}

为了保持用户界面的响应能力,我使用了async / await方法。

根据我的理解,我现在可以在SaveTestRunAsync()中执行一些冗长的操作而不会阻止用户界面,因为它是使用await关键字解耦的。

private async Task<bool> SaveTestRunAsync()
{
    //System.Threading.Thread.Sleep(5000); --> this blocks the UI
    await Task.Delay(5000); // this doesn't block UI

    return true;
}

您能否解释一下为什么拨打Thread.Sleep仍会阻止用户界面,Task.Delay没有?

4 个答案:

答案 0 :(得分:14)

代码仍在UI线程上运行。

它没有在后台线程上运行。

因此,您在该异步方法中执行的任何冗长,昂贵的操作仍将在该段时间内阻止UI。

Thread.Sleep使UI线程进入休眠状态。

在这种情况下,您需要了解asyncawait的工作原理。

await这里基本上是这样说的:

  

让我们将方法分成两部分。第一部分是在await之前执行的任何内容。第二部分是等待对象完成后应执行的任何部分。

因此,基本上,该方法会一直执行,直到达到await Task.Delay(5000);。然后它会延迟5秒&#34;进入游戏,然后说&#34;安排剩余的代码在完成时执行&#34;。然后它返回,以便UI线程可以继续传送消息。

5秒后,UI线程执行该方法的其余部分。

基本上,如果您执行异步I / O,这是很好的,但如果您执行昂贵的操作(如处理大型数据集或类似操作)则不太好。

那么你怎么能这样做呢?

您可以使用Task.Run

这将启动另一个线程来执行它给出的委托,并且UI线程可以在此期间自由地执行其他操作。

基本上你可以考虑使用await的方法:

Life of method:    <------------------------------------------------------->
Parts:             [ start of method ----][awaitable][ rest of method -----]

因此该方法将执行第一部分,直到达到await X,然后它将检查X是否已经完成,如果它没有完成,它将设置一些Task对象以awaitable对象运行的方式运行,一旦完成,该方法的其余部分就会被运行。开始运行。

如果X已经完成,也许它已经完成了异步I / O操作,或者它完成得非常快,那么该方法将继续执行方法的其余部分,就好像你没有在那里写await

但如果没有,则返回。这很重要,因为它允许UI线程(在这种情况下)回到用户点击鼠标点击和事物等消息。

一旦任务等待&#34;等待对象&#34;被告知等待已经完成,其余的方法&#34;已安排,基本上(在这种情况下)将消息放入消息队列,要求它执行该方法的其余部分。

您在这种方法中使用的await语句越多,分数就越多,基本上它只会将方法分成更多部分。

您可以使用早期.NET版本中引入的Task类来完成所有这些操作。 async / await的整个目的是使编写代码变得更容易,因为在代码对象中包装代码会产生令人遗憾的影响,即将代码内部化,并使代码难以处理像异常和循环一样。

答案 1 :(得分:2)

  

据我了解,我现在可以做一些冗长的操作了   SaveTestRunAsync()不会阻止UI,因为它是分离的   使用await关键字。

async/await并不意味着您可以在不阻止UI的情况下执行“冗长的操作”。相反,当你使用await时,你不是在内部排队在另一个线程上工作,你的代码继续在同一个线程上执行(在你的情况下是它的UI线程),直到命中内部{ {1}}并返回来电者。

awaitThread.Sleep之间的区别在于前者是阻塞调用,它将暂停您的UI线程,直到指定的时间结束。后者将在内部使用Task.Delay并将控制权返回给调用方法。一旦该计时器的时钟过去,它将从中断处继续执行(这是编译器魔法进入的地方)

答案 2 :(得分:1)

Await不会创建一个线程或任务,它只是&#34;简单地&#34;调用等待函数(不应该阻塞,否则调用者将被阻塞,因为它仍然是同步的),要求编译器为等待函数返回的任务生成一个延续,包含&#34; rest&#34 ; await之后的代码,并向调用者返回一个新任务(如果等待函数返回一个任务)。当延续完成时,该任务将被标记为完成。

框架确保延续将在UI线程上运行(如果应用程序有一个),如果您希望能够从异步函数访问UI元素,那么这是必须的。

因此,异步函数不应包含阻塞调用。阻止呼叫应该被包裹&#34;在使用Task.Run()

的任务中

答案 3 :(得分:0)

Async / await是比线程更高级别的抽象。

实际的实现可能使用工作线程来实现异步,但它可能(并且经常)在同一个线程中使用其他同步技术。

因此,如果async / await的当前实现不是基于线程的,那么对Thread.Sleep的调用将阻止UI线程。

相关阅读:It's All About the SynchronizationContext