Domanda

In this MSDN article, the following example code is provided (slightly edited for brevity):

public async Task<ActionResult> Details(int? id)
{
    if (id == null)
    {
        return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
    }

    Department department = await db.Departments.FindAsync(id);

    if (department == null)
    {
        return HttpNotFound();
    }

    return View(department);
}

The FindAsync method retrieves a Department object by its ID, and returns a Task<Department>. Then the department is immediately checked to see if it is null. As I understand it, asking for the Task's value in this manner will block code execution until the value from the awaited method is returned, effectively making this a synchronous call.

Why would you ever do this? Wouldn't it be simpler to just call the synchronous method Find(id), if you're going to immediately block anyway?

È stato utile?

Soluzione

As I understand it, asking for the Task's value in this manner will block code execution until the value from the awaited method is returned, effectively making this a synchronous call.

Not quite.

When you call await db.Departments.FindAsync(id) the task is sent off and the current thread is returned to the pool for use by other operations. The flow of execution is blocked (as it would be regardless of using department right after, if I understand things correctly), but the thread itself is free to be used by other things while you wait for the operation to be completed off-machine (and signalled by an event or completion port).

If you called d.Departments.Find(id) then the thread does sit there and wait for the response, even though most of the processing is being done on the DB.

You're effectively freeing up CPU resources when disk bound.

Altri suggerimenti

I really hate that none of the examples show how it’s possible to wait a few lines before awaiting the task. Consider this.

Foo foo = await getFoo();
Bar bar = await getBar();

Console.WriteLine(“Do some other stuff to prepare.”);

doStuff(foo, bar);

This is the kind of code the examples encourage, and you’re right. There’s little sense in this. It does free the main thread to do other things, like respond to UI input, but the real power of async/await is I can easily keep doing other things while I’m waiting for a potentially long running task to complete. The code above will “block” and wait to execute the print line until we’ve gotten Foo & Bar. There’s no need to wait though. We can process that while we wait.

Task<Foo> foo = getFoo();
Task<Bar> bar = getBar();

Console.WriteLine(“Do some other stuff to prepare.”);

doStuff(await foo, await bar);

Now, with the rewritten code, we don’t stop and wait for our values until we have to. I’m always on the lookout for these kinds of opportunities. Being smart about when we await can lead to significant performance improvements. We’ve got multiple cores these days, might as well use them.

So there's more that happens behind the scenes here. Async/Await is syntactic sugar. First look at the signature of the FindAsync function. It returns a Task. Already you're seeing the magic of the keyword, it unboxes that Task into a Department.

The calling function does not block. What happens is that the assignment to department and everything following the await keyword get boxed into a closure and for all intents and purposes passed to the Task.ContinueWith method (the FindAsync function is automatically executed on a different thread).

Of course there is more that happens behind the scenes because the operation is marshalled back to the original thread (so you no longer have to worry about synchronizing with the UI when performing a background operation) and in the case of the calling function being Async (and being called asynchronously) the same thing happens up the stack.

So what happens is you get the magic of Async operations, without the pitfalls.

No it's not returning immediately. The await makes the method call asynchronous. When FindAsync is called, the Details method returns with a task which is not finished. When FindAsync finishes, it will return its result into the department variable and resume the rest of the Details method.

I like to think of "async" like a contract, a contract that says "i can execute this asynchronously if you need it to, but you can call me like any other synchronous function as well".

Meaning, one developer was making functions and some design decisions led them to make/mark a bunch of functions as "async". The caller/consumer of the functions is at the liberty to use them as they decide. As you say you can either call await right before the function call and wait for it, this way you have treated it like a synchronous function, but if you wanted to, you can call it without await as

Task<Department> deptTask = db.Departments.FindAsync(id);

and after, say, 10 lines down the function you call

Department d = await deptTask;

hence treating it as an asynchronous function.

Its upto you.

"if you're going to immediately block anyway" the anwser is "Yes". Only when you need a fast response, await/async make sense. For example UI thread come to a really async method, UI thread will return and continue to listen to button click, while code below "await" will be excuted by other thread and finally get the result.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
scroll top