我有这样的方法:
public static async Task SaveAllAsync()
{
foreach (var kvp in configurationFileMap)
{
using (XmlWriter xmlWriter = XmlWriter.Create(kvp.Value, XML_WRITER_SETTINGS))
{
FieldInfo[] allPublicFields =
kvp.Key.GetFields(BindingFlags.Public | BindingFlags.Static);
await xmlWriter.WriteStartDocumentAsync();
foreach (FieldInfo fi in allPublicFields)
{
await xmlWriter.WriteStartElementAsync("some", "text", "here");
}
await xmlWriter.WriteEndDocumentAsync();
}
}
}
但我很难跟踪有人打电话SaveAllAsync()
时会发生什么。
我认为会发生这样的事情:
SaveAllAsync()
会将控制权返回给await xmlWriter.WriteStartDocumentAsync();
SaveAllAsync()
(或等待任务)时......会发生什么?在调用之前,SaveAllAsync()
仍然会停留在第一个等待中吗?因为没有涉及线程,我猜是这样的...... 答案 0 :(得分:44)
您可以将await
视为“暂停”async
方法,直到该操作完成。作为一种特殊情况,如果操作已经完成(或非常快),那么await
将不会“暂停”该方法;它会立即继续执行。
因此,在这种情况下(假设WriteStartDocumentAsync
尚未完成),await
将暂停该方法并将未完成的任务返回给调用者。请注意,Task
方法返回的async
表示该方法;当方法完成时,Task
就完成了。
最终,WriteStartDocumentAsync
将完成,这将安排async
方法的其余部分继续运行。在这种情况下,它将执行方法的下一部分,直到下一个await
,再次暂停时等等。最终,async
方法将完成,这将完成{{1返回来表示该方法。
有关详细信息,我有async
/await
intro on my blog。
答案 1 :(得分:23)
斯蒂芬斯回答当然是正确的。这是考虑它的另一种方式,可能有所帮助。
代码块的 continuation 是代码完成后发生的事情。当你遇到await
两件事时。首先,执行中的当前位置成为等待任务的延续。其次,控制离开当前方法并运行其他一些代码。另一个代码可能是第一次调用的延续,或者可能是完全不同的事情,比如说事件处理程序。
但是当
xmlWriter.WriteStartDocumentAsync()
的电话已经完成时;怎么了?当前执行是否中断并返回SaveAllAsync()
?
“完成”这个电话的意思并不清楚。 WriteStartDocumentAsync
可能在I / O完成线程上启动异步写入,并返回表示异步工作的Task
。等待这项任务做了两件事,就像我说的那样。首先,此任务的继续成为代码的当前位置。其次,控制离开当前方法并运行其他一些代码。在这种情况下,任何名为SaveAllAsync
的代码都会运行该调用的延续。
现在让我们假设代码 - SaveAllAsync
的调用者继续运行,并且让我们进一步假设您在具有UI线程的应用程序中,如Windows窗体应用程序或WPF应用程序。现在我们有两个线程:UI线程和IO完成线程。 UI线程正在运行SaveAllAsync
的调用者,该调用者最终返回,现在UI线程正处于处理Windows消息的循环中以触发事件处理程序。
最终IO完成,IO完成线程向UI线程发送一条注释,说明“您现在可以继续执行此任务”。如果UI线程忙,则该消息排队;最终UI线程到达它并调用continuation。控制在第一个await
之后恢复,然后进入循环。
现在调用WriteStartElementAsync
。它再次启动一些代码运行,这取决于IO完成线程上发生的事情(大概是它的工作方式取决于它,但这是一个合理的猜测),它返回表示该工作的Task
,并且UI线程等待该任务。同样,执行中的当前位置被注册为该任务的继续,并且控制返回到调用第一个延续的调用者 - 即UI线程的事件处理器。它继续快乐地处理消息,直到有一天IO线程发出信号并且说嘿,你要求的工作是在IO完成线程上完成的,请调用此任务的继续,所以我们再次绕过循环......
有意义吗?
答案 2 :(得分:0)
每当一个函数'异步'时,意味着当在System.Threading.Tasks.Task上完成'await'时,会发生两件事:
执行中的当前位置成为等待任务的“延续”,这意味着在任务完成后,它将执行必要的操作以确保调用异步方法的其余部分。这项工作可以在特定的线程,一些随机线程池线程或者UI线程中完成,这取决于Task为其“延续”获得的SynchronizationContext。
控件返回到:
如果它是async方法中的第一个await,它将返回原始调用方法,其中System.Threading.Tasks.Task表示异步方法(不是在async方法中创建的任何任务)。它可以忽略它并继续,等待它使用Task.Result / Task.Wait()(小心你不要阻止UI线程),或者甚至对它进行等待,如果它本身是一个异步方法。 / p>
如果它不是async方法中的第一个await,它将返回到执行等待的最后一个Task的“continuation”的任何线程中的任何一个处理程序。
所以回答:
当他们等待SaveAllAsync()(或等待任务)时......会发生什么?
做等待SaveAllAsync()将不会导致它被卡在任何内部等待上。这是因为SaveAllAsync()上的等待只是回退到调用者的任何一个名为SaveAllAsync()的方法,它可以继续发生,就像SaveAllAsync()的内部等待回退到它一样。这使线程保持移动并且能够在稍后的时间(可能)响应请求以运行SaveAllAsync()的第一个内部等待的“延续”:await xmlWriter.WriteStartDocumentAsync()'。这样,一点一点,SaveAllAsync()最终会完成,没有任何东西会卡住。
但是......如果你或者其他一些代码更深入,那么在await返回的任何任务上执行Task.Result / Task.Wait()会导致事情在“延续”尝试时被卡住在与等待代码相同的线程上运行。
答案 3 :(得分:-2)
使用父方法示例的简单答案:
await SaveAllAsync();
string x = ""; // <- this will run only when SaveAllAsync() is done including all awaits