为什么把 Scoped 注入到 Singleton 会导致内存泄露?

1 天前
 drymonfidelia
public class SingletonService
{
    private readonly DbContext _dbContext;

    public SingletonService(DbContext dbContext)
    {
        _dbContext = dbContext;
    }
}

public class SomeController
{
    private readonly SingletonService _singletonService;
    private readonly DbContext _dbContext;

    public SomeController(SingletonService singletonService, DbContext dbContext)
    {
        _singletonService = singletonService;
        _dbContext = dbContext;
    }
}

昨天晚上群里别人在讨论的问题,他们说这样注入会导致 SingletonService 里的 DbContext 释放不掉。我不是很理解,Singleton 每次运行都是同一个,SingletonService 里的 DbContext 永远只会创建一次,为什么会内存泄漏?

例子是 C#的,别的有依赖注入的语言应该也一样。

745 次点击
所在节点    程序员
11 条回复
crysislinux
1 天前
我觉得没问题,又不会持续增长
timethinker
1 天前
就你贴出的这个例子而言,是的,DbContext 永远只会创建一次。

但是这种做法是有问题的,仅就这个例子而言,DbContext 不是线程安全的,这意味着当多个线程同时操作一个 DbContext 对象可能会引发数据竞争问题,破坏其内部状态的一致性。

微软官网是这样描述 DbContext 的生命周期的:The lifetime of a DbContext begins when the instance is created and ends when the instance is disposed. A DbContext instance is designed to be used for a single unit-of-work. This means that the lifetime of a DbContext instance is usually very short.

工作单元模式简单的来讲可以理解为对应于数据库的一个事务范围,在这个事务范围进行的操作会被追踪然后被提交。除非你确实有理由要一直保持一个 DbContext 的实例,并且考虑了线程安全问题,那么这么做就没什么问题。
cuso45h2o
1 天前
DbContext 是只会创建一次,但是因为它不会被 dispose ,如果它的代码有问题产生了大量 tracked entities ,这一个 DbContext 也会导致内存泄露。
irisdev
1 天前
@cuso45h2o 楼主,请教个问题,如果像 op 这样写,后续请求造成的 changes 会 append 在这个单例的 dbcontext 上吗
ch3nz
1 天前
会“可能导致内存泄漏和行为异常”而不是“一定导致”
你的例子是属于“一定导致”。
比如你给数据库一顿操作,dbContext track 了所有数据库变化,本该释放,但是被 singleton 持有而不能释放。
irisdev
1 天前
记得.net 不允许在 singleton 中引用 scope 吧,这么写不会报错吗
cuso45h2o
1 天前
回复#4 如果在 Singleton 里发生的更改会累积。
#6 Microsoft.Extensions.DependencyInjection 会检测这种情况,如果把 scoped 注入到 Singleton 会报错 Cannot consume scoped service from singleton 。因为 OP 没说用的是什么 DI 接口,我假设 OP 自己实现了一个简易 DI ,这样注入会导致潜在的内存泄露的问题。
@irisdev
irisdev
1 天前
@cuso45h2o 明白了,谢谢解惑!
nikenidage1
1 天前
是的 楼上很多人都说了。
所以.net 的默认 DI 不允许这么做……
gbw1992
23 小时 36 分钟前
看到了这个例子,突然想起来知乎中一个回答说,ef 至今没有 "解决" 跨线程的 bug
ne6rd
16 小时 3 分钟前
另外补充一点,一般不推荐在 Singleton 里用 Scoped 的类,但是非要用的话,也是有方法的。
不能直接注入,但是可以注入一个 IServiceScopeFactory, CreateScope(), 然后 scope.ServiceProvider.GetServices<>();
这个拿到的 Scoped 实例只能在 method 级别里访问,不能把它存到实例属性上给其他 method 共享。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/1096526

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX