一次又一次,我看到它说async- await使用不会创建任何其他线程。这是没有道理的,因为一台计算机似乎一次只能完成一件事情的唯一方法是
async
await
因此,如果async- await这些都不起作用,那么如何使应用程序响应呢?如果只有1个线程,则调用任何方法都意味着在完成其他操作之前先等待该方法完成,并且该方法内的方法必须在继续执行之前等待结果,依此类推。
实际上,异步/等待并不是那么神奇。整个主题非常广泛,但是对于您的问题,我想我们可以解决,但需要快速而完整的答案。
让我们处理一个Windows Forms应用程序中的简单按钮单击事件:
public async void button1_Click(object sender, EventArgs e) { Console.WriteLine("before awaiting"); await GetSomethingAsync(); Console.WriteLine("after awaiting"); }
我要 明确 不是 谈论什么它GetSomethingAsync是返回现在。我们只说这将在2秒后完成。
GetSomethingAsync
在传统的非异步环境中,您的按钮单击事件处理程序将如下所示:
public void button1_Click(object sender, EventArgs e) { Console.WriteLine("before waiting"); DoSomethingThatTakes2Seconds(); Console.WriteLine("after waiting"); }
当您单击表单中的按钮时,该应用程序将冻结约2秒钟,而我们等待此方法完成。发生的事情是基本上阻塞了一个“消息泵”,使其成为一个循环。
该循环不断询问Windows:“是否有人做过某些事情,例如移动鼠标,单击某些东西?我需要重新粉刷一些东西吗?如果是,请告诉我!” 然后处理那个“东西”。此循环收到一条消息,提示用户单击“ button1”(或Windows中的等效消息类型),并最终调用了button1_Click上面的方法。在此方法返回之前,此循环现在一直处于等待状态。这需要2秒钟,在此期间,不会处理任何消息。
button1_Click
与窗口打交道的大多数事情都是使用消息完成的,这意味着,如果消息循环停止泵送消息,即使只是一秒钟,用户很快就会注意到它。例如,如果将记事本或任何其他程序移到自己程序的顶部,然后再次移开,则会向您的程序发送一连串的绘画消息,指示现在突然又可以看到的窗口区域。如果处理这些消息的消息循环正在等待某些东西(已阻塞),则不会完成绘制。
那么,如果在第一个示例中,async/await不创建新线程,它将如何做呢?
async/await
好吧,发生的是您的方法被一分为二。这是那些广泛的主题类型之一,因此我将不做过多的详细说明,但足以说明该方法分为以下两种情况:
插图:
code... code... code... await X(); ... code... code... code...
重新排列:
code... code... code... var x = X(); await X; code... code... code... ^ ^ ^ ^ +---- portion 1 -------------------+ +---- portion 2 ------+
基本上,该方法执行如下:
到目前为止,我们仍然位于对message1调用的主线程上对button1_Click的原始调用内。 如果导致代码await花费大量时间,则UI仍将冻结。在我们的示例中,没有那么多
实际上,它将使 SynchronizationContext类知道已完成,这将根据当前正在运行的实际同步上下文排队等待执行。Windows Forms程序中使用的上下文类将使用消息循环正在泵送的队列对其进行排队。
对于用户而言,UI现在可以再次响应,可以处理其他按钮单击,调整大小,最重要的是可以 重新绘制 ,因此它似乎不会冻结。
这里有很多活动部件,因此这里有一些指向更多信息的链接,我想说的是“您是否需要它”,但是该主题涉及 面 很广,了解 其中一些活动部件 非常重要。您将始终了解异步/等待仍然是一个漏水的概念。一些潜在的局限性和问题仍然会泄漏到周围的代码中,如果没有,您通常最终不得不调试似乎无缘无故地随机中断的应用程序。
OK,那么如果GetSomethingAsync启动一个将在2秒内完成的线程该怎么办?是的,那么显然有一个新的线索在起作用。但是,此线程不是 由于 此方法的异步性,而是因为此方法的程序员选择了一个线程来实现异步代码。几乎所有异步I / O 都不 使用线程,它们使用不同的东西。async/await 本身 不会增加新线程,但是显然“等待的事情”可以使用线程来实现。
.NET中有很多东西不一定独立运行一个线程,但它们仍然是异步的:
SomethingSomethingAsync
BeginSomething
EndSomething
IAsyncResult
通常,这些东西不使用引擎盖下的螺纹。
好吧,所以您想要一些“广泛的主题”?
好吧,让我们向Roslyn咨询一下我们的按钮单击:
尝试罗斯林
我不会在这里链接完整的生成类,但这是很糟糕的事情。