异步/等待.在哪里执行方法的可等待部分的继续? [英] async/await. Where is continuation of awaitable part of method performed?

查看:26
本文介绍了异步/等待.在哪里执行方法的可等待部分的继续?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我真的很好奇 async/await 如何使您的程序不被停止.我真的很喜欢 Stephen Cleary 如何解释 async/await:我喜欢把await"看作是异步等待".也就是说,async 方法会暂停,直到 awaitable 完成(所以它等待),但实际线程并没有被阻塞(所以它是异步的))."

I am really curious how async/await enables your program not to be halted. I really like the way how Stephen Cleary explains async/await: "I like to think of "await" as an "asynchronous wait". That is to say, the async method pauses until the awaitable is complete(so it waits), but the actual thread is not blocked (so it's asynchornous)."

我读过 async 方法同步工作,直到编译器遇到 await 关键字.好.如果编译器无法找出可等待的,则编译器将等待的和让步控制权排入调用方法 AccessTheWebAsync 的方法中. OK.在调用者(本例中的事件处理程序)内部,处理模式继续进行.在等待结果之前,调用者可能会执行不依赖于 AccessTheWebAsync 结果的其他工作,或者调用者可能会立即等待.事件处理程序正在等待 AccessTheWebAsync,而 AccessTheWebAsync 正在等待 GetStringAsync.让我们看看一个msdn示例:

I've read that async method works synchronously till compilator meets await keywords. Well. If compilator cannot figure out awaitable, then compilator queues the awaitable and yield control to the method that called method AccessTheWebAsync. OK. Inside the caller (the event handler in this example), the processing pattern continues. The caller might do other work that doesn't depend on the result from AccessTheWebAsync before awaiting that result, or the caller might await immediately. The event handler is waiting for AccessTheWebAsync, and AccessTheWebAsync is waiting for GetStringAsync. Let's see an msdn example:

async Task<int> AccessTheWebAsync()
{ 
    // You need to add a reference to System.Net.Http to declare client.
    HttpClient client = new HttpClient();

    // GetStringAsync returns a Task<string>. That means that when you await the 
    // task you'll get a string (urlContents).
    Task<string> getStringTask = client.GetStringAsync("http://msdn.microsoft.com");

    // You can do work here that doesn't rely on the string from GetStringAsync.
    DoIndependentWork();

    // The await operator suspends AccessTheWebAsync. 
    //  - AccessTheWebAsync can't continue until getStringTask is complete. 
    //  - Meanwhile, control returns to the caller of AccessTheWebAsync. 
    //  - Control resumes here when getStringTask is complete.  
    //  - The await operator then retrieves the string result from getStringTask. 
    string urlContents = await getStringTask;

    // The return statement specifies an integer result. 
    // Any methods that are awaiting AccessTheWebAsync retrieve the length value. 
    return urlContents.Length;
}

msdn 博客的另一篇文章async/await 不会创建新线程或使用线程池中的其他线程.好的.

我的问题:

  1. async/await 在哪里执行可等待代码(在我们的示例中下载网站)导致控制权交给我们程序的下一行代码,而程序只询问 Task 的结果;getStringTask?我们知道没有新线程,没有线程池是不使用的.

  1. Where does async/await execute awaitable code(in our example downloading a web site) cause control yields to the next row of code of our program and program just asks result of Task<string> getStringTask? We know that no new threads, no thread pool are not used.

我的愚蠢假设是正确的,即 CLR 只是在一个线程的范围内在彼此之间切换当前的可执行代码和方法的可等待部分?但是更改加数的顺序不会改变总和,并且 UI 可能会被阻塞一段时间.

Am I right in my silly assumption that CLR just switches the current executable code and awaitable part of the method between each other in scope of one thread? But changing the order of addends does not change the sum and UI might be blocked for some unnoticeable time.

推荐答案

async/await 在哪里执行可等待代码(在我们的示例中下载网站)导致控制权交给我们程序的下一行代码,而程序只询问 Task getStringTask 的结果?我们知道没有新线程,没有线程池没有使用.

Where does async/await execute awaitable code(in our example downloading a web site) cause control yields to the next row of code of our program and program just asks result of Task getStringTask? We know that no new threads, no thread pool are not used.

如果操作真的是异步的,那么就没有代码可以执行".您可以将其视为通过回调进行处理;发送 HTTP 请求(同步),然后 HttpClient 注册一个回调,该回调将完成 Task.当下载完成时,回调被调用,完成任务.它比这更复杂一点,但这是总体思路.

If the operation is truly asynchronous, then there's no code to "execute". You can think of it as all being handled via callbacks; the HTTP request is sent (synchronously) and then the HttpClient registers a callback that will complete the Task<string>. When the download completes, the callback is invoked, completing the task. It's a bit more complex than this, but that's the general idea.

我有一篇博文详细介绍了异步操作如何成为无线程的.

I have a blog post that goes into more detail on how asynchronous operations can be threadless.

我的愚蠢假设是正确的,即 CLR 只是在一个线程的范围内在彼此之间切换当前可执行代码和方法的可等待部分吗?

Am I right in my silly assumption that CLR just switches the current executable code and awaitable part of the method between each other in scope of one thread?

这是一个部分真实的心理模型,但它是不完整的.一方面,当 async 方法恢复时,它的(以前的)调用堆栈不会随之恢复.所以 async/awaitfibers 非常不同或 co-routines,即使它们可以用来完成类似的事情.

That's a partially true mental model, but it's incomplete. For one thing, when an async method resumes, its (former) call stack is not resumed along with it. So async/await are very different than fibers or co-routines, even though they can be used to accomplish similar things.

与其将 await 视为切换到其他代码",不如将其视为返回未完成的任务".如果调用方法调用await,那么它也会返回一个不完整的任务,等等.最终,你要么将一个不完整的任务返回给一个框架(例如,ASP.NET).NET MVC/WebAPI/SignalR,或单元测试运行程序);或者您将拥有一个 async void 方法(例如,UI 事件处理程序).

Instead of thinking of await as "switch to other code", think of it as "return an incomplete task". If the calling method also calls await, then it also returns an incomplete task, etc. Eventually, you'll either return an incomplete task to a framework (e.g., ASP.NET MVC/WebAPI/SignalR, or a unit test runner); or you'll have an async void method (e.g., UI event handler).

当操作正在进行时,您最终会得到一个任务对象的堆栈".不是真正的堆栈,只是一个依赖树.每个async 方法都由一个任务实例表示,它们都在等待该异步操作完成.

While the operation is in progress, you end up with a "stack" of task objects. Not a real stack, just a dependency tree. Each async method is represented by a task instance, and they're all waiting for that asynchronous operation to complete.

方法的可等待部分的继续执行在哪里?

Where is continuation of awaitable part of method performed?

当等待一个任务时,await 将 - 默认情况下 - 在捕获的上下文中恢复它的 async 方法.这个上下文是SynchronizationContext.Current,除非它是null,在这种情况下它是TaskScheduler.Current.实际上,这意味着在 UI 线程上运行的 async 方法将在该 UI 线程上恢复;处理 ASP.NET 请求的 async 方法将继续处理相同的 ASP.NET 请求(可能在不同的线程上);在大多数其他情况下,async 方法将在线程池线程上恢复.

When awaiting a task, await will - by default - resume its async method on a captured context. This context is SynchronizationContext.Current unless it is null, in which case it is TaskScheduler.Current. In practice, this means that an async method running on a UI thread will resume on that UI thread; an async method handling an ASP.NET request will resume handling that same ASP.NET request (possibly on a different thread); and in most other cases the async method will resume on a thread pool thread.

在您问题的示例代码中,GetStringAsync 将返回一个不完整的任务.下载完成后,该任务将完成.因此,当 AccessTheWebAsync 对该下载任务调用 await 时,(假设下载尚未完成)它将捕获其当前上下文,然后从 AccessTheWebAsync.

In the example code for your question, GetStringAsync will return an incomplete task. When the download completes, that task will complete. So, when AccessTheWebAsync calls await on that download task, (assuming the download hasn't already finished) it will capture its current context and then return an incomplete task from AccessTheWebAsync.

当下载任务完成时,AccessTheWebAsync 的延续将被安排到那个上下文(UI 线程、ASP.NET 请求、线程池,...),它会提取Length 在该上下文中执行时的结果.当 AccessTheWebAsync 方法返回时,它会设置之前从 AccessTheWebAsync 返回的任务的结果.这反过来又会恢复下一个方法,等等.

When the download task completes, the continuation of AccessTheWebAsync will be scheduled to that context (UI thread, ASP.NET request, thread pool, ...), and it will extract the Length of the result while executing in that context. When the AccessTheWebAsync method returns, it sets the result of the task previously returned from AccessTheWebAsync. This in turn will resume the next method, etc.

这篇关于异步/等待.在哪里执行方法的可等待部分的继续?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆