强制使用.Close()和using(...)后,为什么SqlConnection仍然是打开状态?

6
我正在进行一些ADO.Net和EF的实验来更好地了解它如何处理SQL Server连接。在ADO.Net中,我发现了一些非常有趣的事情。我创建了多个任务,调用一个简单的插入SQL脚本,并以适当的等待来处理SqlConnection和SqlCommand。在这里没有什么特别之处,但当这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个连接,并在可执行文件保持打开状态的同时保持这些连接打开状态? - dyslexicanaboko
我一直在使用控制台应用程序进行测试,我认为我已经回答了自己的问题。默认连接池最大值为100个连接。如果您通过90个任务打开90个连接,那么您将拥有90个保持打开状态的连接,只要您的可执行文件运行时间大约为4-8分钟,根据MS文档。我确实看到这种情况发生。令人惊讶的是,因为我真的以为“close”表示关闭。 - dyslexicanaboko
1个回答

6
默认情况下,ADO.Net使用连接池。从文档中可以看出(重点在于):
连接池减少了必须打开新连接的次数。池管理器拥有物理连接的所有权。它通过保持每个给定连接配置的一组活动连接来管理连接。每当用户在连接上调用Open时,池管理器会在池中查找可用连接。如果有可用的池连接,则返回它而不是打开新连接。当应用程序在连接上调用Close时,池管理器将其返回到活动连接的池集合中,而不是关闭它。一旦连接被返回到池中,它就可以在下一个Open调用上被重用。

准确地说,在执行此测试时,我知道连接池的存在,但我不知道即使在SqlConnection被处理后它仍然保持活动状态。我已更改上面的代码,将连接字符串中的Pooling=False,循环10k次后连接确实被删除了。 - RollRoll
@DavidG 我遇到了类似的问题,即使设置了Pooling = False,实际运行的进程仍然在我关闭和释放连接以及退出整个程序后继续运行。我大致计时约为6.4分钟,GC才抓住它。有什么想法吗? - Rick Riggs

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