使用 Entity Framework Core 和 ThreadPool 而不达到最大 SQL Server 连接

Posted

技术标签:

【中文标题】使用 Entity Framework Core 和 ThreadPool 而不达到最大 SQL Server 连接【英文标题】:Use Entity Framework Core with ThreadPool without reaching maximum SQL Server connection 【发布时间】:2021-07-14 05:54:32 【问题描述】:

我尝试向我的 SQL Server 插入大量数据而未达到最大连接数。

我已经尝试在我的连接字符串中最大化池大小,如下所示:

optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=Echo;Max Pool Size=5000;Pooling=True;");

是否可以为每个线程使用一个连接?

这是我的代码

    public static void GenerateMeridien()
    
        List<Parallele> paralleleList = null;

        using (var context = new EchoContext())
        
            paralleleList = context.Paralleles.ToList();
        

        foreach (Parallele parallele in paralleleList)
        
            ThreadPool.QueueUserWorkItem(new WaitCallback(MeridienTaskCallback), parallele);
        
    

    private static void MeridienTaskCallback(object paralleleObj)
    
        Parallele parallele = (Parallele)paralleleObj;

        // The context always reaches maximum connection because the garbage collector is not fast enough
        using (var context = new EchoContext())
        
            List<Square> squareList = SquareLogic.GenerateSquare(parallele);
            context.Squares.AddRange(squareList);

            context.SaveChanges();
            Console.Write("*");
        
    

【问题讨论】:

正确使用IDisposable(通常通过using)否定“垃圾收集器未运行”。究竟是什么连接(暂时)泄漏? 请提供一些数字:数据量、频率等。 @tymtam 在他的回答中提到的问题是您的代码试图同时并行运行所有插入。解决方案是在任何时候简单地限制工人的数量。 顺便说一句,您根本没有理由使用多线程。如果你切换到异步/等待,你将能够运行这个“单线程/异步/并发”。 获取适当的 BulkCopy 库并在一批中插入所有内容。 【参考方案1】:

选项 A。并行工作,保存一次

每次计算后都需要保存吗?你可以并行计算并保存吗?

List<Parallele> paralleleList = null;

using (var context = new EchoContext())

    paralleleList = context.Paralleles.ToList();


List<Square> squares = DoAllWorkInParallel(paralleleList);

// Save once:
using (var context = new EchoContext())

    context.Squares.AddRange(squares);
    context.SaveChanges();

选项 B. 一直保存

我认为你应该使用Parallel.ForEachMaxDegreeOfParallelism 或使用Task.WhenAll

Parallel.ForEach

例如:

public static async Task GenerateMeridien()

    List<Parallele> paralleleList = null;

    using (var context = new EchoContext())
    
        paralleleList = context.Paralleles.ToList();
    

    Parallel.ForEach(
        source: paralleleList,
        parallelOptions: new ParallelOptionsMaxDegreeOfParallelism = x,
        body: parallele => Work(parallele));

    


private static void Work(Parallele parallele)

    using (var context = new EchoContext())
    
        List<Square> squareList = SquareLogic.GenerateSquare(parallele);
        context.Squares.AddRange(squareList);

        await context.SaveChanges();
    

Task.WhenAll

public static async Task GenerateMeridien()

    List<Parallele> paralleleList = null;

    using (var context = new EchoContext())
    
        paralleleList = context.Paralleles.ToList();
    

    var tasks = new List<Task>();
    foreach (Parallele parallele in paralleleList)
    
        tasks.Add(WorkAsync(parallele));
    

    await Task.WhenAll(tasks.ToArray());


private static async Task WorkAsync(Parallele parallele)

    using (var context = new EchoContext())
    
        List<Square> squareList = SquareLogic.GenerateSquare(parallele);
        context.Squares.AddRange(squareList);

        await context.SaveChangesAsync();
    

【讨论】:

以上是关于使用 Entity Framework Core 和 ThreadPool 而不达到最大 SQL Server 连接的主要内容,如果未能解决你的问题,请参考以下文章

Entity Framework Core 性能优化

在 Entity Framework Core 中使用 [ComplexType]

在 Entity Framework Core 中使用 SQL 视图

使用 Entity Framework Core 更新相关数据

使用 Entity Framework Core 自动增加部分主键

如何使用 Entity Framework Core 模拟异步存储库