这是我的事件处理程序代码:
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
没有?
答案 0 :(得分:14)
代码仍在UI线程上运行。
它没有在后台线程上运行。
因此,您在该异步方法中执行的任何冗长,昂贵的操作仍将在该段时间内阻止UI。
Thread.Sleep
使UI线程进入休眠状态。
在这种情况下,您需要了解async
和await
的工作原理。
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}}并返回来电者。
await
和Thread.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线程。