“Async All the Way Down”: Well, what's all the

2019-04-04 01:39发布

I'm trying to fully understand async-await and one of the gaps in my understanding is seeing what is "All the Way Down." I create an async method, it is called by another async method, etc., all the way down to something that I understand in vague terms like "a UI" or "a web server that can handle multiple requests". How would I describe in technical terms what is "all the way down"?

So let's take the second example of a web server. Say I have a controller action like

[HttpGet]
public async Task<IHttpActionResult> GetRecords()
{
    var records = await repository.GetRecordsFromDbAsync();
    return Ok(records);
}

Where can I find in the .NET source code the "all the way down" code that enables this to be called asynchronously?

5条回答
倾城 Initia
2楼-- · 2019-04-04 01:58

It is not clear from your question where you found that quotation, or in what context it is being used. However, "async all the way down" is generally understood to mean that you shouldn't switch back and forth between synchronous and asynchronous methods in a single logical operation.

If something is to be done asynchronously, it should be consistently asynchronous, at all levels—thus, "all the way down" (and up) the hierarchy/call stack/etc. In other words, it never makes sense to do a synchronous, blocking call at any point in asynchronous code, because then it isn't really asynchronous at all.

From the .NET Parallel Programming team's blog entry, "Should I expose synchronous wrappers for asynchronous methods?":

Async All the Way Down

The point here is that you need to be extremely careful when wrapping asynchronous APIs as synchronous ones, as if you’re not careful, you could run into real problems. If you ever find yourself thinking you need to call an asynchronous method from something that’s expecting a synchronous invocation (e.g. you’re implementing an interface which has a synchronous method on it, but in order to implement that interface you need to use functionality that’s only exposed asynchronously), first make sure it’s truly, truly necessary; while it may seem more expedient to wrap “sync over async” rather than to re-plumb this or that code path to be asynchronous from top to bottom, the refactoring is often the better long-term solution if it’s possible.

As for your question about where can this be found in the .NET source code, MSalters has already answered this. Fundamentally, the .NET APIs will call down to system-level APIs provided by the Windows operating system. These perform asynchronous I/O, and signal the caller when they have completed. You don't really need to understand how this works on a technical level, though; that is the whole point of the abstraction.

查看更多
时光不老,我们不散
3楼-- · 2019-04-04 02:01

The phrase "async all the way down" is a bit misleading, because it usually refers to the fact that once you use an async method, you need to have async methods all the way up (or back, depending on your mental image) - from your async method to its caller, and then your caller's caller, and so forth, all the way back.

Your example shows a WebApi/MVC controller that exposes an async task. The next step in the async chain is the WebApi/MVC infrastructure that receives the HTTP GET request, maps it to a controller, and dispatches the call to the controller's method. This infrastructure is async aware, meaning it knows to call async controller methods properly and await their result to return the HTTP response.

As to how exactly that infrastructure is implemented, I don't specifically know, nor care - I know that ASP.NET web services support async Task controllers, and that's good enough for me.

查看更多
Bombasti
4楼-- · 2019-04-04 02:04

GetRecordsFromDbAsync is an async method, so your top level async method (which is called by the async-supporting ASP.NET web server) just hands over its asyncness to the next level.

All the way down until GetRecordsFromDbAsync or its descendants actually calls the last async method in the call stack. There it presumably becomes native and will it register an I/O interrupt or something else which gets called when the file is read, the web request is handled, etc.

查看更多
何必那么认真
5楼-- · 2019-04-04 02:14

Every procedural programming language is a series of function / procedure calls, with one function / procedure calling another. It is possible to represent this sequence of calls from procedure to procedure using a call graph see Wikipedida for a starting point for call graphs. These graphs generally show the sequence of procedural calls starting at the top and proceeding to the bottom.

I quickly produced a diagram of a partial call graph for an ASP .NET MVC application. The diagram is only a partial representation for an ASP .NET MVC application because it omits things such as the initial receipt of the request by the operating system (eg. Windows), web server (eg. IIS) and various components of ASP .NET that are responsible for processing a HTTP request as it travels through the ASP .NET Request processing pipeline. These matters can can be omitted for the purposes of this discussion. Although it's worthwhile noting that they would sit at the top of the call graph, because they deal with the initial stages of handling the HTTP request, and ultimately, at some point ASP .NET ultimately invokes an action of a controller.

As you can see in the diagram I've represented the action of the controller that would be invoked by ASP .NET as an async action. On the left hand side of the call graph is a series of async procedure calls. Ultimately it arrives at the box that is the subject of your question.

The answer to the question that is consistent with the notion "All the way down" is that "I am an async method". What does this async method do? Well, that depends on what you want to do? If you're reading or writing a file then it's an async call to read or write a file. Are you making a database query? Then the call is to an async method that makes that query. With this in mind I guess you can say that often what is at the bottom will be a device driver method, that performs an IO access asynchronously. Although it could easily be a long running compute bound asynchronous operation that you wish to perform such as processing an image, or video file.

It's worthwhile noting the right hand side of the call graph here too. Although often you may want to call async methods all the way down, the right hand branch of this call graph shows that you don't necessarily need to call an async method at the bottom at all. enter image description here

查看更多
男人必须洒脱
6楼-- · 2019-04-04 02:22

All the way down would be the Win32 API. At that level, async I/O is done via OVERLAPPED objects, and alertable waits such as WaitForMultipleObjectsEx.

查看更多
登录 后发表回答