HokieMike
HokieMike

Reputation: 675

Async EF 6 vs wrapped Sync EF

So.. this may be a dumb question. I can't get my head completely around WHY using EF6 Async would improve performance vs wrapping sync EF6 calls in a Task (assume db calls are within a Web API REST api method)

I.e., why is this:

//wrapping synch with asynch
return await Task.Run(() =>
{
    var albums = this.context.Albums
                .Where(x => x.Artist.ID == artist.ID)
                .ToList();
    return albums;
});

worse than this:

//using async 
return await this.context.Albums
            .Where(x => x.Artist.ID == artist.ID)
            .ToListAsync();

Note: I have read this article http://blogs.msdn.com/b/pfxteam/archive/2012/03/24/10287244.aspx which seems to say (oversimplified) "don't just wrap a synchronous method, rewrite the method more efficiently".

Question 1, is that what the EF6 async implementation has done? I assume it is using async I/O in it's implementation?

Question 2 (and my real question) - can someone explain WHY this is better? Won't both implementations result in free-ing up the request thread to handle other requests until the db operation is done?

Upvotes: 1

Views: 268

Answers (1)

Servy
Servy

Reputation: 203802

Let's say that you're baking a big holiday dinner. There's going to be tons of food, including a main course of a big chicken. It's going to take a long time to cook, so you prep it and put it in the oven early, set a timer to check back on it later, and then go on preparing the next dish (mashed potatoes).

You can't just sit there and wait (synchronously) for that chicken to finish; you need to let it cook (asynchronously) while you go off to prep all of the other dishes. If you waited for the chicken to finish before starting work on anything else, in addition to you being super bored, the chicken would be ice cold by the time you finished with everything else.

Now there are several ways you could make this operation (letting the chicken cook) happen while you do other work. For example, you could set a timer and go check on it as soon as the timer notified you that the time was up. Another solution is to not work on your own. You could go grab your son and tell him to just sit in front of the stove waiting for the chicken to be ready and have him notify you when it's ready.

Using the timer is analogous to a single threaded asynchronous solution. (Each person is a thread.) There is only one worker (you), and you start asynchronous work, have asynchronous notifications of when you need to do things, but are only ever working on one thing at a time. Having multiple people in the kitchen is analogous to a multi-threaded application. If you're using those multiple people to actually do multiple tasks that require an actual person to do work at the same time (say, you're mashing potatoes while your son is washing the asparagus), then you're getting your work done faster. When you're using that extra person (thread) to do nothing but sit there and wait for something to finish, while you go off to do work, then you're only wasting that person's (thread's) time; they'd rather go off and do something productive.

So to completely strip away the analogies, when you use Task.Run to perform IO synchronously you're scheduling work in the thread pool where the allocated thread needs to just sit there doing nothing (instead of doing actual productive work) while it waits for the IO to finish. When you just use the inherently asynchronous IO operations then no other threads are involved at all.

Upvotes: 5

Related Questions