多个dbcontexts在并行线程中,EntityException "当活动用户较少时重新运行您的语句"

9

我正在使用Parallel.ForEach在多个线程上进行工作,每次迭代都使用一个新的EF5 DbContext,并将其全部包装在TransactionScope中,如下所示:

using (var transaction = new TransactionScope())
{
    int[] supplierIds;

    using (var appContext = new AppContext())
    {
        supplierIds = appContext.Suppliers.Select(s => s.Id).ToArray();
    }

    Parallel.ForEach(
        supplierIds,
        supplierId =>
    {
        using (var appContext = new AppContext())
        {
            Do some work...

            appContext.SaveChanges();                
        }
    });

    transaction.Complete();
}

运行几分钟后,它会抛出一个EntityException异常,错误信息为"The underlying provider failed on Open",并包含以下详细信息:

"SQL Server数据库引擎实例此时无法获取LOCK资源。当活跃用户较少时,请重新运行您的语句。请询问数据库管理员检查该实例的锁定和内存配置,或检查是否存在长时间运行的事务。"

有人知道是什么原因导致这个问题,或者如何避免这个问题吗?谢谢。


你可能正在创建大量线程,TPL(任务并行库)针对短时间、CPU密集型任务进行了优化,而非CPU密集型任务会使其困惑(“为什么有这么多空闲的CPU?”),从MSDN使用有限的并发调度程序。 - ta.speot.is
http://msdn.microsoft.com/en-us/library/ee789351.aspx - ta.speot.is
我认为你说得对,它产生了大量的线程。正如bmdixon建议的那样限制并行度解决了这个问题。 - Matthew Sharpe
采纳的答案已更改 :-) - Matthew Sharpe
3个回答

6

您也可以尝试使用Parallel.ForEach()方法中的new ParallelOptions { MaxDegreeOfParallelism = 8 }来设置最大并发任务数(将8替换为您想要限制的数字)。

有关更多详细信息,请参见MSDN


这不会影响默认的TPL任务调度程序中的任何内容。 - ta.speot.is
1
@ta.speot.is 不确定你的意思。Parallel.ForEach()不会为集合中的每个项创建一个Task,它会重用Task。如果设置了MaxDegreeOfParallelism,它将限制使用的Task数量,从而限制实际并行度。 - svick
在Entity Framework的并行操作中出现了意外争用的问题。我也遇到过这个问题,这里也讨论过:https://dev59.com/wGYs5IYBdhLWcg3wDPmt - xpereta

1
你还需要找出为什么应用程序会占用如此大量的锁定资源?你在多个数据库连接周围包装了一个TransactionScope。这可能会导致分布式事务,这可能与此有关。它肯定会导致锁定资源直到最后才被释放。请进行更改。
你只能将锁定限制提高到一定程度。它不适用于任意数量的供应商ID。你需要找到锁定资源的原因,而不是缓解症状。

实际上,我不明白为什么这需要大量的锁。 - Matthew Sharpe
1
我的怀疑是您同时有数百个连接(您明白为什么吗?),每个连接都有“正常”的锁定量。解决方法是将事务范围移动到并行循环体中。尝试在测试目的下删除它。 - usr

0
你正在遇到 SQL Server 允许的最大锁定数 - 默认情况下自动设置并由可用内存控制 - 的限制。
你可以:
  1. 手动设置它-我不确定具体方法,但 Google 是你的朋友。
  2. 为 SQL Server 添加更多内存。
  3. 更频繁地提交事务。

请原谅我的无知,但我不明白为什么这个程序需要很多锁?谢谢你的帮助。 - Matthew Sharpe
正如usr所提到的,为每个插入操作创建一个事务范围内的上下文将导致大量连接,因此很可能是这个原因。如果在运行之前重新启动SQL实例,它是否会崩溃? - Matt Randle
再仔细想想,几乎可以确定是这个问题。使用单个连接并进行大量插入操作会升级锁定。但如果使用多个连接,则不会出现这种情况。 - Matt Randle
更新:不幸的是,即使没有TransactionScope,错误仍然会发生。这在某种程度上削弱了我对该问题的理解。 - Matthew Sharpe
抱歉,我不是100%清楚它是什么。也许与它没有快速处理上下文有关,因为它正在循环中?您是否在标准for循环中遇到相同的问题? - Matt Randle
FYI,问题已经通过bmdixon的解决方案限制并行度得到解决。正如ta.speot.is所建议的那样,我怀疑它正在生成大量线程,因此连接也很多。 - Matthew Sharpe

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接