【问题标题】:Connection leak (C#/ADO.NET) even though SqlConnection created with using连接泄漏(C#/ADO.NET),即使 SqlConnection 使用创建
【发布时间】:2020-05-15 13:17:01
【问题描述】:

我有一个程序在线程池上运行的任务中加载大量数据(每次迭代约 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 unexpectedly running before all tasks complete

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();
        }
    }

【问题讨论】:

  • 另外,这是带有 WinForms UI 的 .NET 4.6.2
  • 老实说,我不确定我是否可以考虑一些环境因素(例如,查询中使用的主表超过 20 billion 行,其中有几个指数,并且该程序在更多消费者常见的硬件上运行良好)。我还将更新这个问题,以包括我的另一个问题中的按钮单击过程,这样它们就在一个地方,因为这两个形成了 MRE 代码。然后,我将简要更新一下,为任何希望测试的人提供建议的数据库架构和字段名称。
  • 这不是一个答案,但是:SqlCommandSqlReader 也实现了IDisposable 模式 - 所以你应该这样做using(var con...) {using(var cmd...){using var reader...){}}} 这样,你不需要关闭或处置其中任何一个明确地,using 语句正在为您执行此操作。另外,你不应该使用AddWithValue,你应该为你的参数使用唯一的名字。

标签: c# ado.net connection-pooling sqlconnection


【解决方案1】:

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-08-17
    • 1970-01-01
    • 2019-12-04
    • 2023-03-19
    • 2015-11-20
    • 1970-01-01
    相关资源
    最近更新 更多