Ale*_*lex 47 .net c# multithreading entity-framework async-await
我经常使用async/await来确保ASP.NET MVC Web API线程不被长时间运行的I/O和网络操作阻止,特别是数据库调用.
该System.Data.Entity的命名空间提供各种帮助这里的扩展,如FirstOrDefaultAsync,ContainsAsync,CountAsync等等.
但是,由于数据上下文不是线程安全的,这意味着以下代码存在问题:
var dbContext = new DbContext();
var something = await dbContext.someEntities.FirstOrDefaultAsync(e => e.Id == 1);
var morething = await dbContext.someEntities.FirstOrDefaultAsync(e => e.Id == 2);
Run Code Online (Sandbox Code Playgroud)
事实上,我有时会看到例外情况:
System.InvalidOperationException:未关闭连接.连接的当前状态是打开的.
那么正确的模式是否using(new DbContext...)为每个对数据库的异步调用使用单独的块?相反,执行同步可能更有益吗?
Ste*_*ary 60
该DataContext班是SQL LINQ的一部分.它不理解async/ awaitAFAIK,不应与Entity Framework async扩展方法一起使用.
只要您使用EF6或更高版本,该DbContext课程就可以正常async使用; 但是,每次DbContext运行一个实例只能有一个操作(同步或异步).如果您的代码实际上正在使用DbContext,那么检查您的异常的调用堆栈并检查是否有任何并发使用(例如Task.WhenAll).
如果您确定所有访问都是顺序访问,那么请发布一个最小的repro和/或将其作为错误报告给Microsoft Connect.
nos*_*tio 32
我们这里陷入僵局.AspNetSynchronizationContext,它负责ASP.NET Web API执行环境的线程模型,并不保证之后的异步延续await将发生在同一个线程上.这样做的整个想法是使ASP.NET应用程序更具可伸缩性,因此ThreadPool使用挂起的同步操作可以阻止更少的线程.
但是, DataContext该类(LINQ to SQL的一部分)
不是线程安全的,因此不应该在DataContextAPI调用可能发生线程切换的地方使用它.一个单独的using每个异步调用构造将不会帮助,无论是:
var something;
using (var dataContext = new DataContext())
{
something = await dataContext.someEntities.FirstOrDefaultAsync(e => e.Id == 1);
}
Run Code Online (Sandbox Code Playgroud)
那是因为DataContext.Dispose可能在与最初创建对象的线程不同的线程上执行,这不是DataContext预期的.
如果你想坚持使用DataContextAPI,同步调用它似乎是唯一可行的选择.我不确定该语句是否应扩展到整个EF API,但我认为使用DataContextAPI 创建的任何子对象也可能不是线程安全的.因此,在ASP.NET中,它们的using范围应限于两个相邻await调用之间的范围.
将一堆同步DataContext调用卸载到一个单独的线程可能很诱人await Task.Run(() => { /* do DataContext stuff here */ }).但是,这是一个已知的反模式,特别是在ASP.NET的上下文中,它可能只会损害性能和可伸缩性,因为它不会减少完成请求所需的线程数.
不幸的是,虽然ASP.NET的异步架构很棒,但它仍然与一些已建立的API和模式不兼容(例如,这是类似的情况).这特别令人难过,因为我们这里没有处理并发API访问,即只有一个线程试图同时访问一个DataContext对象.
希望Microsoft能够在未来的Framework版本中解决这个问题.
[更新]虽然大规模,但可以将EF逻辑卸载到单独的进程(作为WCF服务运行),这将为ASP.NET客户端逻辑提供线程安全的异步API.这个过程可以使用自定义同步上下文作为事件机器进行编排,类似于Node.js. 它甚至可以运行类似Node.js的公寓池,每个公寓都保持EF对象的线程亲和力.这将允许仍然从异步EF API中受益.
[更新]以下是尝试找到此问题的解决方案.