.NetCore EFCore 多线程生命周期管理
1、多线程下EFCore的DbContext实例处理
DbContext生命周期默认注入是Scope,每一次请求时创建一个实例,在当前请求的上下文中共用,当请求结束后,释放生命周期,释放数据库链接。若开启多线程,在不同的线程中使用同一个DbContext上下文,则报错如下:System.InvalidOperationException: A second operation was started on this context instance before a previous operation completed. This is usually caused by different threads concurrently using the same instance of DbContext. For more information on how to avoid threading issues with DbContext, see https://go.microsoft.com/fwlink/?linkid=2097913.代码演示如下:
解决办法: 在GetAsync中重新创建DbContext(new SecondHandDbContext),不使用请求注入的DbContext:
2、多线程下生命周期已被释放
生命周期为Scope方式,随着请求的结束,实例生命周期也会被释放,因此在多线程下若共享实例,容易出现实例已释放的错误,报错如下:Instances cannot be resolved and nested lifetimes cannot be created from this LifetimeScope as it (or one of its parent scopes) has already been disposed.Autofac at Autofac.Core.Lifetime.LifetimeScope.BeginLifetimeScope(Object tag)。通过注入IServiceProvider,
解决办法:
1、在构造函数注入IServiceProvider,应用中通过IServiceProvider的GetService方法获取实例,最终发现IServiceProvider也已被释放,IServiceProvider只是当前请求的实例,该方法失败。
2、从当前应用中获取实例,构造函数注入IHost,通过host获取IServiceProvider:
在线程中通过_sp创建新的生命周期:
using (var sp = _sp.CreateScope())
{
await Task.Delay(5000);
var applyReposity = sp.ServiceProvider.GetRequiredService<IApplyReposity>();
Console.WriteLine($"taskCode:{applyReposity.GetHashCode()},taskThreaId:{Thread.CurrentThread.ManagedThreadId}");
var model = await applyReposity.GetAsync(c => 1 == 1);
sp.Dispose();
}
观察curThreaId与taskThreaId,Task.Run开启了新的线程,与当前主线程线程ID不同,当前请求中注入的IApplyReposity的实例_applyReposity编码是57588670,新线程中开启的新生命周期sp.ServiceProvider中获取的实例编码是2837748;主线程与子线程实例不再相关,解决主线程已释放,子线程获取不到实例的问题。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 记一次.NET内存居高不下排查解决与启示