[英].NET async\await fundamentals
在使用.NET 4.5 async \\ await框架后,我有一个问题。
我们来看看这个程序( msdn示例 ):
async Task<int> AccessTheWebAsync()
{
HttpClient client = new HttpClient();
Task<string> getStringTask = client.GetStringAsync("http://msdn.microsoft.com");
DoIndependentWork();
string urlContents = await getStringTask;
return urlContents.Length;
}
void DoIndependentWork()
{
resultsTextBox.Text += "Working . . . . . . .\r\n";
}
该程序将按以下顺序运行:
我花了一些时间来理解的一件事是GetStringAsync
方法尽管它的名称同步运行(名称约定实际上是误导性的)。
在我们异步运行该方法的过程中,我们需要显式地使用Task.Run
或Task.Factory.StartNew
。
但真正的问题是,如果我们有独立的工作,为什么不马上做,而不是等待等待从GetStringAsync调用? (换句话说,为什么异步方法不能按定义异步运行?)
编辑:我将改写第二和第三个操作:
(2)GetStringAsync同步启动。
(3)在某些时候,GetStringAsync调用await和线程分叉,控件返回AccessTheWebAsync。
我花了一些时间来理解的一件事是GetStringAsync方法尽管它的名称同步运行(名称约定实际上是误导性的)。
那是不对的。 GetStringAsync
返回Task<string>
。 它将立即返回,这意味着DoIndependentWork
将(可能)在下载完成之前运行。 await
运算符将异步等待,直到GetStringAsync
返回的Task<T>
完成。
但真正的问题是,如果我们有独立的工作,为什么不马上做,而不是等待等待从GetStringAsync调用? (换句话说,为什么异步方法不能按定义异步运行?)
返回方法后,异步方法的实际“工作”正在运行。 返回的Task
将(通常)处于未完成的状态,这意味着该方法仍然是异步运行的。 您可以检查Task.IsCompleted
以进行验证。
试试这个:
Task<string> getStringTask = client.GetStringAsync("http://msdn.microsoft.com");
Debug.WriteLine("Completed? {0}", getStringTask.IsCompleted); // Will likely print false
DoIndependentWork();
string urlContents = await getStringTask;
Debug.WriteLine("Completed now? {0}", getStringTask.IsCompleted); // Will always print true
每个async
方法在每个await
分成段。 每个段将成为编译器生成的状态机上的状态。
每个await
指令适用于一个awaitable对于这一个Task
是最常见的情况。
每个状态/段将同步执行,直到检查接收到的等待的位置为止已完成。
如果等待完成,它将继续执行下一个状态。
如果等待未完成并且没有当前的SynchronizationContext
,则执行将被阻止,直到等待完成为止,此时将开始执行下一状态。
如果存在当前的SynchronizationContext
,则执行将返回给调用者,并且当等待完成时,将继续到下一状态将被发布到捕获的SynchronizationContext
。
程序等待返回的字符串(如果操作没有完成则阻塞)。
await
不会阻止await getStringTask
。 它将在编译器生成的状态机对象中保存AccessTheWebAsync
方法的内部状态(局部变量和执行点所在的位置),并返回调用AccessTheWebAsync
的外部方法。 状态将被恢复,并且当getStringTask
任务完成后,将通过编译器生成的延续回调异步恢复执行。 如果您熟悉C#迭代器和yield
关键字,则await
状态机控制流与它非常相似,尽管迭代器是同步执行的。
await
之后执行将如何恢复取决于启动await
的线程的同步上下文 。 如果它是一个抽取Windows消息的UI线程,则可能会在线程消息循环的下一次迭代(通常在Application.Run
)调用延续回调。 如果它是非UI线程(例如,控制台应用程序),则继续可能在不同的线程上发生。 因此,虽然您的方法的控制流仍然是逻辑线性的,但从技术上讲它不是(如果您只是执行getStringTask.Wait()
而不是await getStringTask
)。
程序等待返回的字符串(如果操作没有完成则阻塞)。
不,它没有。 await
等待 ,但它不会阻止任何线程。 这就是重点。
我花了一些时间来理解的一件事是
GetStringAsync
方法尽管它的名称同步运行(名称约定实际上是误导性的)。
那是错的。 大多数方法都是异步运行的。 它很可能在开始时也有一个小的同步部分,但这应该可以忽略不计。
在我们异步运行该方法的过程中,我们需要显式地使用
Task.Run
或Task.Factory.StartNew
。
不,该方法已经异步运行。 如果你想在另一个线程上运行小同步部分,那么你可以使用Task.Run()
,但这几乎没有任何意义。 此外,不要使用StartNew()
,它不适用于异步方法。
换句话说,为什么异步方法不能按定义异步运行?
关于await
的重要一点是它在它离开的相同环境中恢复。
这在GUI应用程序中非常有用,您经常需要修改UI,然后执行异步操作,然后再次修改UI。 如果async
自动意味着整个方法在ThreadPool
线程上执行,那么这是不可能的。
此外,这样做更有效率,因为您不必为需要很少时间的事情切换到另一个线程。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.