5

I searched a lot about these subjects, but I'm still not sure it works as expected. And why.

What I understand:

  • While processing a web request, you are using one of IIS thread from its pool. Within that request if you use an async call to, for example, query some data from a database, you free up that thread, so that IIS can use that same thread to process another call. That’s good. Probably.
  • Sometime later when the database finally gives the awaited data, the code resumes. The async documentation mention that you now COULD be in another thread. Or not. DotNet's decision. If it's in the same thread as before, that’s ok.
  • I use Dependency Injection to inject and close the context on a PerRequest lifetime (using Microsoft Unity). Closing is my main concern here. It works perfectly in my synchronous world: the dbcontext is closed at the end of my web request.
  • EntityFramework is known to have its DbContext NOT thread safe

Question 1: Now if the resuming code is in another thread, is it from the same thread pool IIS has to process all requests or Is it from another side-pool?

Question 2: If the code runs in another thread, what about the WebRequest context? Will the DI track correctly the end of that deferred call and not call Dispose() before the async code is really over?

Question 3: If I use the async methods of EntityFramework, like ToListAsync or FirstOrDefaultAsync I read everywhere that "It should be ok". Can someone elaborate on that? Does EF track specifically the web request or the initial thread? Is there some sort of capture happening? Will my dbcontext be mixed up with another web request reusing my initial thread?

Question 4: If I use the normal (sync) methods of EntityFramework but wrapped within a Task. Whats gonna happen? Is it still "It should be ok"?

Sorry, that’s a lot of questions, its been bothering me for a long time now.

Sunny Jangid
  • 578
  • 4
  • 19
  • Are you talking about asp.net core or older versions of asp.net? – Evk Nov 02 '17 at 12:02
  • Older version. Let's say we are in version 4.7. Does it actuallay make a difference ? – Eric Craeymeersch Nov 07 '17 at 09:41
  • 2
    It's too compicated (long) to answer this question properly. But main point is that even if request switches between threads, asp.net (starting from asp.net 4.5) will be able to keep track of that (via its SynchronizationContext) and each thread will have correct http context which will flow from one thread to another. Request will not end after thread switch. As for EF and thread-safety - if you use it from one thread at a time (even if there are multiple threads, but only one of them uses it at any given time) - you are fine. – Evk Nov 07 '17 at 09:46
  • 2
    But if you would do something like `var t1 = Task.Run(() => use context here);var t2 = Task.Run(() => again use context);await Task.WhenAll(t1, t2)` then you might get into trouble, because you potentially access context from multiple threads _at the same time_. So all you need to know is that _request_ and _thread_ are not the same. One request might be handled by different threads (at least in non-anchient versions of asp.net). – Evk Nov 07 '17 at 09:56
  • Your answer changed everything. With this new SynchronizedContext keyword, i was able to better understand the whole situation. Now i feel stupid. So i'm gonna try to answer my own question. – Eric Craeymeersch Nov 07 '17 at 10:18

1 Answers1

0

Well, i can now try to answer my own questions.

Question 1: From Do asynchronous operations in ASP.NET MVC use a thread from ThreadPool on .NET 4

Yes - all threads come from the thread-pool. Your MVC app is already multi-threaded, when a request comes in a new thread will be taken from the pool and used to service the request. That thread will be 'locked' (from other requests) until the request is fully serviced and completed. If there is no thread available in the pool the request will have to wait until one is available. If you have async controllers they still get a thread from the pool but while servicing the request they can give up the thread, while waiting for something to happen (and that thread can be given to another request) and when the original request needs a thread again it gets one from the pool.

Question 2: If the code runs in another thread, what about the WebRequest context?

From ewk and http://vegetarianprogrammer.blogspot.fr/2012/12/understanding-synchronizationcontext-in.html And https://msdn.microsoft.com/en-us/magazine/gg598924.aspx

The SynchronisationContext will make sure the Web Request (HttpContext.Current) is properly set on the current thread while resuming execution. This is part of ASP.NET background work.

Will the DI track correctly the end of that deferred call and not call Dispose() before the async code is really over?

Since the Web request flows on possibly multiple threads, and does not end with the initial thread, I don't see any reason why the EndRequest event would fire before the total flow is completed anymore. Thus, I see no reason anymore why a PerRequestLifeTimeManager or another PerRequest strategy would fail by calling some Dispose() before it's needed.

Question 3 à 4 : Async and EF. As ewk mentionned, Entity Framework is safe as long as you don't use it in different thread at the same time. As question 3 and 4 all shows different ways of hitting the DbContext, the answer is : To be safe, there should be only one simultaneous access to the DbContext whatever the way of runnning the tasks. As ewk mentionned, explicitely spinning 2 tasks using the same dbcontext is NOT safe.