首页 > 解决方案 > 连接泄漏 (C#/ADO.NET) 即使使用使用创建的 SqlConnection

问题描述

我有一个程序在线程池上运行的任务中加载大量数据(每次迭代约 800K-1M 行)(请参阅下面的违规代码示例);同时运行的任务不超过 4 个。这是程序中唯一与该数据库建立连接的地方。在我的笔记本电脑(和其他同事相同的笔记本电脑)上运行该程序时,该程序运行良好。但是,我们可以通过远程桌面访问另一个工作站,它比我们的笔记本电脑更强大。该程序在其列表中失败了大约 1/3 到 1/2。所有任务都返回异常。

第一个例外是:“超时已过期。在从池中获取连接之前已经过了超时期限。这可能是因为所有池连接都在使用中并且达到了最大池大小。” 我尝试过在 StackOverflow 上搜索、搜索、搜索,并将头撞在桌子上,试图弄清楚这是怎么回事。一次运行的任务不超过 4 个,任何时候都不应有超过 4 个连接。

作为对此的回应,我尝试了两件事:(1)我在 conn.Open() 行周围添加了一个 try/catch,如果 InvalidOperationException 出现,它将清除池 - 这似乎有效[没有让它运行所有通过,但大大超过了以前的水平],但以性能为代价。(2) 我将 ConnectionTimeout 更改为 30 秒而不是 15 秒,这不起作用(但让它继续进行一点)。我也曾尝试过 ConnectRetryInterval=4(错误地选择了这个而不是 ConnectRetryCount)——这导致了另一个错误“最大请求数为 4,800”,这很奇怪,因为我们仍然不应该接近 4,800请求或连接。

简而言之,我很茫然,因为我无法弄清楚是什么导致了这种连接泄漏,只有在更高速度的计算机上。我也无法让该计算机上的 Visual Studio 直接调试 - 任何人可能对在哪里尝试解决这个问题的任何想法都将不胜感激。

(后续c#TaskFactory ContinueWhenAll在所有任务完成前意外运行

private void LoadData()
    {
        SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder();
        builder.DataSource = "redacted";
        builder.UserID = "redacted";
        builder.Password = "redacted";
        builder.InitialCatalog = "redacted";
        builder.ConnectTimeout = 30;

        using (SqlConnection conn = new SqlConnection(builder.ConnectionString))
        {
            //try
            //{
            //    conn.Open();
            //} catch (InvalidOperationException)
            //{
            //    SqlConnection.ClearPool(conn);
            //    conn.Open();
            //}
            conn.Open();


            string monthnum = _monthsdict.First((x) => x.Month == _month).MonthNum;
            string yearnum = _monthsdict.First((x) => x.Month == _month).YearNum;

            string nextmonthnum = _monthsdict[Array.IndexOf(_monthsdict, _monthsdict.First((x) => x.Month == _month))+1].MonthNum;
            string nextyearnum = _monthsdict[Array.IndexOf(_monthsdict, _monthsdict.First((x) => x.Month == _month)) + 1].YearNum;

            SqlCommand cmd = new SqlCommand();

            cmd.Connection = conn;

            cmd.CommandText = @"redacted";
            cmd.Parameters.AddWithValue("@redacted", redacted);
            cmd.Parameters.AddWithValue("@redacted", redacted);
            cmd.Parameters.AddWithValue("@redacted", redacted);
            cmd.CommandTimeout = 180;

            SqlDataReader reader = cmd.ExecuteReader();
            while(reader.Read())
            {
                Data data = new Data();

                int col1 = reader.GetOrdinal("col1");
                int col2 = reader.GetOrdinal("col2");
                int col3 = reader.GetOrdinal("col3");
                int col4 = reader.GetOrdinal("col4");

                data.redacted = redacted;
                data.redacted = redacted;
                data.redacted = redacted;
                data.redacted = redacted;
                data.redacted = redacted;

                data.Calculate();
                _data.Add(data); //not a mistake, referring to another class variable
            }
            reader.Close();
            cmd.Dispose();
            conn.Close();
            conn.Dispose();
        }
    }

标签: c#ado.netconnection-poolingsqlconnection

解决方案


事实证明,这是一个没有仔细阅读文档的经典案例。我试图使用 ThreadPool.SetMaxThreads 将最大线程数限制为 4,但最大线程数不能小于处理器数。在它失败的工作站上,它有 8 个处理器。因此,从来没有上限,它运行任务计划程序认为合适的任务数量,最终达到连接池限制。

https://docs.microsoft.com/en-us/dotnet/api/system.threading.threadpool.setmaxthreads


推荐阅读