异步 Web 请求、EntityFramework 和 DI,它是如何工作的?
Posted
技术标签:
【中文标题】异步 Web 请求、EntityFramework 和 DI,它是如何工作的?【英文标题】:Asynchronous Web Request, EntityFramework, and DI, how does it work? 【发布时间】:2018-04-14 21:50:54 【问题描述】:我搜索了很多关于这些主题的内容,但我仍然不确定它是否能按预期工作。以及为什么。
我的理解:
在处理 Web 请求时,您正在使用其池中的 IIS 线程之一。在该请求中,例如,如果您使用异步调用从数据库中查询某些数据,您将释放该线程,以便 IIS 可以使用同一线程来处理另一个调用。那挺好的。可能。 稍后,当数据库最终提供等待的数据时,代码会继续执行。异步文档提到您现在可以在另一个线程中。或不。 DotNet 的决定。如果它和以前在同一个线程中,那没关系。 我使用依赖注入在 PerRequest 生命周期内注入和关闭上下文(使用 Microsoft Unity)。关闭是我主要关心的问题。它在我的同步世界中完美运行:dbcontext 在我的 Web 请求结束时关闭。 众所周知,EntityFramework 的 DbContext 不是线程安全的问题 1:现在如果恢复代码在另一个线程中,它是来自同一个线程池 IIS 必须处理所有请求还是来自另一个边池?
问题 2:如果代码在另一个线程中运行,那么 WebRequest 上下文呢? DI 是否会正确跟踪延迟调用的结束,而不是在异步代码真正结束之前调用 Dispose()?
问题 3:如果我使用 EntityFramework 的异步方法,例如 ToListAsync 或 FirstOrDefaultAsync,我到处都读到“应该没问题”。有人可以详细说明吗? EF 是否专门跟踪 Web 请求或初始线程?是否发生了某种捕获?我的 dbcontext 是否会与另一个重用我的初始线程的 Web 请求混淆?
问题 4:如果我使用 EntityFramework 的普通(同步)方法但包装在一个任务中。会发生什么?还是“应该没问题”吗?
抱歉,问题太多了,困扰我很久了。
【问题讨论】:
你说的是 asp.net core 还是旧版本的 asp.net? 旧版本。假设我们在 4.7 版本中。它真的有影响吗? 这个问题太复杂(太长),无法正确回答。但要点是,即使请求在线程之间切换,asp.net(从 asp.net 4.5 开始)将能够跟踪(通过其 SynchronizationContext)并且每个线程都将具有正确的 http 上下文,该上下文将从一个线程流出给另一个。线程切换后请求不会结束。至于 EF 和线程安全 - 如果你一次从一个线程使用它(即使有多个线程,但在任何给定时间只有一个线程使用它) - 你很好。 但如果您要执行var t1 = Task.Run(() => use context here);var t2 = Task.Run(() => again use context);await Task.WhenAll(t1, t2)
之类的操作,那么您可能会遇到麻烦,因为您可能会同时从多个线程访问上下文。所以你只需要知道 request 和 thread 是不一样的。一个请求可能由不同的线程处理(至少在非高级版本的 asp.net 中)。
你的回答改变了一切。使用这个新的 SynchronizedContext 关键字,我能够更好地了解整个情况。现在我觉得自己很愚蠢。所以我会试着回答我自己的问题。
【参考方案1】:
好吧,我现在可以尝试回答我自己的问题了。
问题 1: 来自Do asynchronous operations in ASP.NET MVC use a thread from ThreadPool on .NET 4
是的 - 所有线程都来自线程池。您的 MVC 应用程序已经是多线程的,当请求进入时,将从池中取出一个新线程并用于为请求提供服务。该线程将被“锁定”(来自其他请求),直到请求得到完全服务并完成。如果池中没有可用的线程,则请求将不得不等到有可用的线程。 如果您有异步控制器,它们仍然从池中获取一个线程,但是在为请求提供服务时,它们可以放弃线程,同时等待某些事情发生(并且可以将该线程提供给另一个请求)以及原始请求需要线程时再次它从池中得到一个。
问题2:如果代码在另一个线程中运行,那么WebRequest上下文呢?
来自 ewk 和 http://vegetarianprogrammer.blogspot.fr/2012/12/understanding-synchronizationcontext-in.html 还有https://msdn.microsoft.com/en-us/magazine/gg598924.aspx
SynchronisationContext 将确保 Web 请求 (HttpContext.Current) 在当前线程上正确设置,同时恢复执行。这是 ASP.NET 后台工作的一部分。
DI 是否会正确跟踪延迟调用的结束,而不是在异步代码真正结束之前调用 Dispose()?
由于 Web 请求可能在多个线程上流动,并且不会以初始线程结束,因此我看不出 EndRequest 事件会在整个流程完成之前触发的任何原因。因此,我看不出为什么 PerRequestLifeTimeManager 或其他 PerRequest 策略会在需要之前调用一些 Dispose() 而失败。
问题 3 à 4:异步和 EF。正如 ewk 所提到的,只要您不在不同的线程中同时使用实体框架,它就是安全的。由于问题 3 和 4 都显示了访问 DbContext 的不同方式,因此答案是:为了安全起见,无论运行任务的方式如何,都应该只有一个同时访问 DbContext。正如 ewk 所提到的,使用相同的 dbcontext 显式旋转 2 个任务是不安全的。
【讨论】:
以上是关于异步 Web 请求、EntityFramework 和 DI,它是如何工作的?的主要内容,如果未能解决你的问题,请参考以下文章
csharp 标准脚手架EntityFramework与异步操作WebAPI控制器