【问题标题】:Why is the SqlConnection still open after forcing .Close() and with using(...)?为什么在强制 .Close() 和 using(...) 之后 SqlConnection 仍然打开?
【发布时间】:2018-03-04 01:58:15
【问题描述】:

我正在对 ADO.Net 和 EF 进行一些实验,以便更好地了解它如何处理 SQL Server 连接。

我在 ADO.Net 中发现了一些非常有趣的东西。我正在创建多个任务,这些任务调用一个简单的插入 SQL 脚本,并适当地等待处理 SqlConnectionSqlCommand。这里没什么特别的,但是当 10k 个任务完成处理时,所有 SQL 连接仍然挂起(我通过运行sp_who 确认)。清除这些连接的唯一方法是在应用程序实例关闭时。

这怎么可能?我尝试了很多方法来强制它关闭,= null 数据访问实例 + 强制 GC 但没有...

我试图从这种行为中理解,但我失败了。有什么线索吗?

static void Main(string[] args)
{
    Console.WriteLine(DateTime.Now.ToString("HH:mm:ss"));

    for (int i = 0; i < 10000; i++)
    {
        Task.Run(() =>
            {
                var dbLegacy = new DataAccessLegacy();
                dbLegacy.TableBInsert();
                dbLegacy = null;
            });
    }

    Console.ReadKey();
}

public void TableBInsert()
{
    using (SqlConnection connection = new SqlConnection(@"Password=qpqp;Persist Security Info=True;User ID=sqlUser2;Initial Catalog=DatabaseA;Data Source=VM2HOSTNAME\VM2INSTANCEA"))
    {
        using (SqlCommand command = new SqlCommand("DatabaseBInsert", connection))
        {
            command.CommandType = CommandType.StoredProcedure;

            command.Parameters.Add("ColAInt", SqlDbType.Int);
            command.Parameters[0].Value = (new Random()).Next(0, 5000);

            command.Parameters.Add("ColBTinyInt", SqlDbType.TinyInt);
            command.Parameters[1].Value = (new Random()).Next(1, 255);

            command.Parameters.Add("ColCVarchar", SqlDbType.VarChar);
            command.Parameters[2].Value = Convert.ToChar((new Random()).Next(1, 255)).ToString();

            command.Parameters.Add("ColDVarcharMax", SqlDbType.VarChar);
            command.Parameters[3].Value = Convert.ToChar((new Random()).Next(1, 255)).ToString();

            command.Parameters.Add("ColEDecimal", SqlDbType.Decimal);
            command.Parameters[4].Value = (new Random()).Next(0, 5000) + 0.5;

            command.Parameters.Add("ColFSmallInt", SqlDbType.SmallInt);
            command.Parameters[5].Value = (new Random()).Next(0, 5000);

            command.Parameters.Add("ColGDateTime", SqlDbType.DateTime);
            command.Parameters[6].Value = DateTime.Now;

            command.Parameters.Add("ColHChar", SqlDbType.Char);
            command.Parameters[7].Value = Convert.ToChar((new Random()).Next(1, 255)).ToString();

            command.Parameters.Add("ColINVarchar", SqlDbType.NVarChar);
            command.Parameters[8].Value = Convert.ToChar((new Random()).Next(1, 255)).ToString();

            command.Parameters.Add("ColJNChar", SqlDbType.NChar);
            command.Parameters[9].Value = Convert.ToChar((new Random()).Next(1, 255)).ToString();

            connection.Open();
            command.ExecuteScalar();
            connection.Close();

            command.Dispose();
        }

        connection.Dispose();
    }
}

【问题讨论】:

  • 自从提出这个问题后,您对此是否有更清楚的了解?提供的答案实际上并没有回答您的核心问题。我有同样的问题,为什么当我运行 90 个任务时,只要可执行文件保持打开状态,就会打开 90 个连接并保持打开状态?
  • 我一直在使用控制台应用程序进行测试,我想我已经回答了我自己的问题。默认连接池最大为 100 个连接。如果您通过 90 个任务打开 90 个连接,您将有 90 个打开的连接,只要您的可执行文件根据 MS Docs 运行大约 4-8 分钟,这些连接就会保持打开状态。我确实看到了这种情况。令人惊讶,因为我真的认为接近意味着接近。

标签: c# sql-server sqlconnection


【解决方案1】:

默认情况下,ADO.Net 使用connection pooling。来自文档(强调我的):

连接池减少了必须打开新连接的次数。 pooler 维护物理连接的所有权。它通过为每个给定的连接配置保持一组活动连接来管理连接。每当用户在连接上调用 Open 时,池化程序都会在池中查找可用的连接。如果池连接可用,则将其返回给调用者,而不是打开新连接。 当应用程序在连接上调用 Close 时,池化器会将其返回到池中的活动连接集,而不是关闭它。一旦连接返回到池中,就可以在下一次 Open 调用中重用它。

【讨论】:

  • 准确地说,我在执行此测试时知道连接池,但我不知道即使在 SqlConnection 被处置后它仍然存在。我将上面的代码更改为连接字符串中的 Pooling=False ,并且在 10k 循环完成后确实删除了连接。
  • @DavidG 我遇到了类似的问题,即使设置Pooling = False,在我关闭并处理连接并完全退出我的程序后,实际运行的进程仍然在运行。在 GC 抓住它之前,我将它松散地计时在大约 6.4 分钟。有什么想法吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-09-11
  • 2015-09-30
  • 1970-01-01
  • 2020-08-02
  • 2019-10-11
  • 2018-09-26
相关资源
最近更新 更多