C# 单个方法中的多个等待
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/18445279/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Multiple Awaits in a single method
提问by Xenoprimate
I have a method like this:
我有一个这样的方法:
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();
}
}
}
But I'm struggling to follow what will happen when someone calls SaveAllAsync()
.
但我正在努力了解当有人打电话时会发生什么SaveAllAsync()
。
What I think will happen is this:
我认为会发生的是:
- When someone first calls it,
SaveAllAsync()
will return control to the caller at the lineawait xmlWriter.WriteStartDocumentAsync();
- Then... When they await
SaveAllAsync()
(or wait for the task)... What happens? WillSaveAllAsync()
still be stuck on the first await until that is called? Because there's no threading involved, I guess that is the case...
- 当有人第一次调用它时,
SaveAllAsync()
会将控制权返回给线路上的调用者await xmlWriter.WriteStartDocumentAsync();
- 然后……当他们等待
SaveAllAsync()
(或等待任务)时……会发生什么?将SaveAllAsync()
仍然是第一的await坚持到它被调用?因为没有涉及线程,我想是这样......
采纳答案by Stephen Cleary
You can think of await
as "pausing" the async
method until that operation is complete. As a special case, if the operation is already completed (or is extremelyfast), then the await
will not "pause" the method; it will continue executing immediately.
您可以将其await
视为“暂停”该async
方法,直到该操作完成。作为一种特殊情况,如果操作已经完成(或非常快),则await
不会“暂停”该方法;它将立即继续执行。
So in this case (assuming that WriteStartDocumentAsync
is not already completed), await
will pause the method and return an uncompleted task to the caller. Note that the Task
returned by an async
method represents that method; when the method completes, then that Task
is completed.
所以在这种情况下(假设WriteStartDocumentAsync
尚未完成),await
将暂停该方法并将未完成的任务返回给调用者。请注意,方法Task
返回的async
代表该方法;当方法完成时,即Task
完成。
Eventually, WriteStartDocumentAsync
will complete, and that will schedule the rest of the async
method to continue running. In this case, it'll execute the next part of the method until the next await
, when it gets paused again, etc. Eventually, the async
method will complete, which will complete the Task
that was returned to represent that method.
最终,WriteStartDocumentAsync
将完成,这将安排async
方法的其余部分继续运行。在这种情况下,它将执行方法的下一部分,直到下一个await
,当它再次暂停时,等等。最终,该async
方法将完成,这将完成Task
返回来表示该方法的 。
For more information, I have an async
/await
intro on my blog.
有关更多信息,我在我的博客上有一个async
/await
介绍。
回答by Eric Lippert
Stephens answer is of course correct. Here's another way to think about it that might help.
斯蒂芬斯的回答当然是正确的。这是另一种思考方式,可能会有所帮助。
The continuationof a hunk of code is what happens after the code completes. When you hit an await
two things happen. First, the current position in the execution becomes the continuation of the awaited task. Second, control leaves the current method and some other code runs. The other code is maybe the continuation of the first call, or maybe is something else entirely, an event handler, say.
大量代码的延续是代码完成后发生的事情。当你击中时,await
会发生两件事。首先,执行中的当前位置成为等待任务的继续。其次,控制离开当前方法并运行其他一些代码。另一个代码可能是第一次调用的延续,或者可能是其他完全不同的东西,比如一个事件处理程序。
But when the call to
xmlWriter.WriteStartDocumentAsync()
has completed; what happens? Is the current execution interrupted and returned back toSaveAllAsync()
?
但是当调用
xmlWriter.WriteStartDocumentAsync()
完成时;发生什么了?当前执行是否中断并返回到SaveAllAsync()
?
It is not clear what you mean by the call "completing". WriteStartDocumentAsync
starts an asynchronous write, probably on an I/O completion thread, and returns you a Task
that represents that asynchronous work. Awaiting that task does two things, like I said. First, the continuation of this task becomes the current position of the code. Second, control leaves the current method and some other code runs. In this case, whatever code called SaveAllAsync
runs the continuation of that call.
不清楚你所说的“完成”是什么意思。WriteStartDocumentAsync
开始异步写入,可能在 I/O 完成线程上,并返回Task
代表该异步工作的 a。等待那个任务有两件事,就像我说的。首先,这个任务的延续成为代码的当前位置。其次,控制离开当前方法并运行其他一些代码。在这种情况下,任何调用SaveAllAsync
的代码都会运行该调用的延续。
Now lets suppose that code -- the caller of SaveAllAsync
continues to run, and lets suppose further that you are in an application with a UI thread, like a Windows Forms application or a WPF application. Now we have two threads: the UI thread and an IO completion thread. The UI thread is running the caller of SaveAllAsync
, which eventually returns, and now the UI thread is just sitting there in a loop handling Windows messages to trigger event handlers.
现在让我们假设代码—— 的调用者SaveAllAsync
继续运行,并进一步假设您在一个具有 UI 线程的应用程序中,如 Windows 窗体应用程序或 WPF 应用程序。现在我们有两个线程:UI 线程和 IO 完成线程。UI 线程正在运行 的调用者SaveAllAsync
,它最终返回,现在 UI 线程只是坐在一个循环中,处理 Windows 消息以触发事件处理程序。
Eventually the IO completes and the IO completion thread sends a note to the UI thread that says "you can run the continuation of this task now". If the UI thread is busy, that message gets queued up; eventually the UI thread gets to it and invokes the continuation. Control resumes after the first await
, and you enter the loop.
最终 IO 完成并且 IO 完成线程向 UI 线程发送一条注释,说“您现在可以运行此任务的延续”。如果 UI 线程很忙,则该消息将排队;最终 UI 线程到达它并调用延续。控制在第一个 之后恢复await
,您进入循环。
Now WriteStartElementAsync
is invoked. It again starts some code running that depends on something happening on the IO completion thread (presumably; how it does its work is up to it, but this is a reasonable guess), that returns a Task
representing that work, and the UI thread awaits that task. Again, the current position in the execution is signed up as the continuation of that task and control returns to the caller that invoked the first continuation -- namely, the UI thread's event processor. It continues merrily processing messages until one day the IO thread signals it and says that hey, the work you asked for is done on the IO completion thread, please invoke the continuation of this task, and so we go around the loop again...
现在WriteStartElementAsync
被调用。它再次启动一些代码运行,这取决于 IO 完成线程上发生的事情(大概;它如何工作取决于它,但这是一个合理的猜测),返回一个Task
表示该工作的代码,并且 UI 线程等待任务。同样,执行中的当前位置被注册为该任务的延续,并且控制返回到调用第一个延续的调用者——即 UI 线程的事件处理器。它继续愉快地处理消息,直到有一天 IO 线程向它发出信号并说嘿,您要求的工作已在 IO 完成线程上完成,请调用此任务的延续,因此我们再次循环......
Make sense?
有道理?
回答by simon hearn
Whenever a function is 'async' it means that when an 'await' is done on a System.Threading.Tasks.Task a two main things happen:
每当一个函数是“异步”时,这意味着当在 System.Threading.Tasks.Task 上完成“await”时,会发生两件主要的事情:
The current position in the execution becomes a "continuation" of the awaited Task, meaning that after the Task is completed it will do what is necessary to make sure the remainder of the async method gets called. This work could be be done in a specific thread, some random thread pool thread or maybe the UI thread, this depends on the SynchronizationContext that the Task gets for its "continuation".
The control is returned to either:
If it is the first await in the async method it returns to the original calling method with a System.Threading.Tasks.Task that represents the async method (NOT any of the Tasks created within the async method). It can just ignore it and carry on, wait for it with a Task.Result/Task.Wait() (careful you don't block the UI thread) or even do an await on it if it itself is an async method.
If it is not the first await in the async method it will just return to whichever handler in whichever thread was executing the "continuation" of the last Task that was awaited.
执行中的当前位置成为等待任务的“延续”,这意味着在任务完成后,它将执行必要的操作以确保异步方法的其余部分被调用。这项工作可以在特定线程、一些随机线程池线程或 UI 线程中完成,这取决于任务为其“延续”获得的 SynchronizationContext。
控制权返回到:
如果它是 async 方法中的第一个 await,它会返回到原始调用方法,并使用 System.Threading.Tasks.Task 表示异步方法(不是在异步方法中创建的任何任务)。它可以忽略它并继续,使用 Task.Result/Task.Wait() 等待它(注意不要阻塞 UI 线程),或者如果它本身是一个异步方法,甚至可以等待它。
如果它不是 async 方法中的第一个等待,它只会返回到正在执行等待的最后一个任务的“继续”的线程中的任何处理程序。
So to answer:
所以要回答:
When they await SaveAllAsync() (or wait for the task)... What happens?
当他们等待 SaveAllAsync()(或等待任务)时......会发生什么?
Doing await SaveAllAsync() won't NECESSARILY cause it to get stuck on any of it's internal awaits. This is because an await on SaveAllAsync() just drops back to the caller of whichever method called SaveAllAsync() which can carry on like nothing happened, just like SaveAllAsync()'s internal awaits dropped back to it. This keeps the thread moving and able to respond (potentially) to the request at some later time to run the "continuation" of SaveAllAsync()'s first internal await: await xmlWriter.WriteStartDocumentAsync()'. This way, bit by bit, SaveAllAsync() will eventually finish and nothing will get stuck.
执行 await SaveAllAsync() 不会必然导致它卡在任何内部等待上。这是因为 SaveAllAsync() 上的等待只是回退到调用 SaveAllAsync() 的任何方法的调用者,它可以像什么也没发生一样继续,就像 SaveAllAsync() 的内部等待回退到它一样。这使线程保持移动并能够在稍后的某个时间响应(可能)请求以运行 SaveAllAsync() 的第一个内部等待的“继续”:await xmlWriter.WriteStartDocumentAsync()'。这样,一点一点地,SaveAllAsync() 最终会完成并且不会卡住任何东西。
BUT... if you OR some other code deeper down ever does a Task.Result/Task.Wait() on any of the Tasks returned by an await this could cause things to get stuck if the "continuation" tries to run on the same thread as the waiting code.
但是...如果您或其他一些更深入的代码曾经在等待返回的任何任务上执行 Task.Result/Task.Wait() 这可能会导致事情卡住,如果“继续”试图在与等待代码相同的线程。
回答by dkostas
A simple answer using parent method example:
使用父方法示例的简单答案:
await SaveAllAsync();
string x = ""; // <- this will run only when SaveAllAsync() is done including all awaits