AddScoped依赖注入与线程安全性问题咨询
AddScoped 行为解析及你的场景问题解答
核心结论
你的理解是正确的:AddScoped 会为每个请求创建独立的 Manager 实例,正常使用下不会出现不同请求共享同一实例导致的线程安全问题。同事的担忧只有在错误使用依赖注入生命周期的情况下才会发生。
AddScoped 的本质行为
在 ASP.NET Core(包括 gRPC 服务)中,Scoped 生命周期的服务有两个核心特性:
- 每个请求(包括每个 gRPC 调用)会生成一个独立的服务实例,该实例仅属于当前请求的上下文。
- 实例的生命周期与请求绑定:请求开始时创建,请求结束时销毁,和线程池的线程复用无关——线程可以被多个请求复用,但每个请求的 Scoped 实例是完全隔离的,不会随线程传递给下一个请求。
结合你的代码场景分析
你的代码是否会出现线程安全问题,核心取决于 MyService 的注册生命周期:
场景1:MyService 注册为 Scoped/Transient
如果 MyService 是按 Scoped 或 Transient 注册(比如 builder.Services.AddScoped<MyService>()):
- 每个 gRPC 请求(无论是调用
SayHello还是SayHelloAgain)都会得到一个全新的MyService实例。 - 每个
MyService实例注入的IManager都是当前请求专属的 Scoped 实例,不同请求的Manager完全独立。 - 此时即使并发请求到来,每个请求修改的都是自己的
AuthValue,不会影响其他请求的DoSomethingAsync或DoSomethingElseAsync执行逻辑,完全不存在线程安全问题。
场景2:MyService 注册为 Singleton
如果 MyService 被错误注册为 Singleton(builder.Services.AddSingleton<MyService>()):
- 整个应用生命周期内只有一个
MyService实例,它在第一次请求时注入的IManager会被所有后续请求复用。 - 此时多个并发请求会共享同一个
Manager实例,当一个请求修改AuthValue时,会影响其他请求中正在执行的异步方法,这才会出现你同事担忧的线程安全问题。但这不是 AddScoped 的问题,而是在 Singleton 服务中注入 Scoped 服务的错误用法,违背了 DI 的生命周期规则。
AddScoped 的正确用法
- 当服务需要保存与当前请求相关的状态(比如用户认证信息、请求上下文数据)时,使用 AddScoped。
- 绝对避免在 Singleton 服务中直接注入 Scoped 服务,如果必须在 Singleton 中使用 Scoped 服务,需要通过
IServiceScopeFactory手动创建作用域:// Singleton 服务中使用 Scoped 服务的正确方式 public class MySingletonService { private readonly IServiceScopeFactory _scopeFactory; public MySingletonService(IServiceScopeFactory scopeFactory) { _scopeFactory = scopeFactory; } public async Task DoWork() { using var scope = _scopeFactory.CreateScope(); var manager = scope.ServiceProvider.GetRequiredService<IManager>(); // 使用 manager 处理当前逻辑 } }
内容的提问来源于stack exchange,提问作者Drakon82
相关产品推荐
相关产品推荐

