4

在我的多线程 Windows 服务中,我在每个线程上打开与数据库的连接,然后处理这些连接,但问题是当我对 sys.sysprocesses 表执行查询时,有些连接没有在数据库上关闭。

我运行了两个单元测试并看到了一些奇怪的行为,首先我运行了一个包含 100 个任务的循环,并且在每个任务中我都打开了一个新连接。这些完成后(我通过 WaitAll() 看到)我看到一些连接仍然挂在数据库中。在第二个单元测试中,当我在没有并行执行的情况下运行几个 open/dispose 时,它​​会很好地处理它们并且我的数据库中没有挂起的连接。

问题是,在我的 Windows 服务中,这些挂起的连接正在被添加,最终没有更多新连接的位置,并且 db 对用户变得不可用。

这是代码,第一个是并行的,第二个不是:

    [TestMethod]
    public void TestMultiThreadedAccessToExplicitObjectContext()
    {
        int taskSize = 100;
        List<Task> taskList = new List<Task>();
        int goodSources = 0;
        for (int i = 0; i < taskSize; i++)
        {
            Task newTask = Task.Factory.StartNew(() =>
            {
                System.Data.Objects.ObjectContext objectContext = new PersoniteEntities();
                objectContext.Connection.Open();
                objectContext.Dispose();
                Thread.Sleep(1200);
            });

            taskList.Add(newTask);
        }

        Task.WaitAll(taskList.ToArray());
        GC.Collect();
        total += goodSources;
    }

[TestMethod]
    public void TestMultiThreadedAccessToExplicitObjectContextInline()
    {
        System.Data.Objects.ObjectContext objectContext1 = new PersoniteEntities();
        objectContext1.Connection.Open();
        objectContext1.Dispose();

        System.Data.Objects.ObjectContext objectContext2 = new PersoniteEntities();
        objectContext2.Connection.Open();
        objectContext2.Dispose();

        System.Data.Objects.ObjectContext objectContext3 = new PersoniteEntities();
        objectContext3.Connection.Open();
        objectContext3.Dispose();

        System.Data.Objects.ObjectContext objectContext4 = new PersoniteEntities();
        objectContext4.Connection.Open();
        objectContext4.Dispose();

        System.Data.Objects.ObjectContext objectContext5 = new PersoniteEntities();
        objectContext5.Connection.Open();
        objectContext5.Dispose();
    }

谢谢

4

2 回答 2

4

使用连接池,每个工作进程(或本例中的线程)都有自己的连接池。因此,如果您将 Max Pool Size 设置为 3,并生成 100 个线程,则您可能拥有 300 个连接。

来自有类似问题的人的更多信息:

ODP.NET 连接池参数

以及来自 MS 的一些关于连接池如何工作的文档,以及对连接池碎片的解释:

MSDN - SQL Server 连接池 (ADO.NET)

解决方案 1

在连接字符串中关闭连接池

MSDN - 连接字符串 - 池

当此键的值设置为 true 时,任何新创建的连接都将在应用程序关闭时添加到池中。在下一次尝试打开同一连接时,将从池中提取该连接。如果连接具有相同的连接字符串,则连接被认为是相同的。不同的连接有不同的连接字符串。

该键的值可以是“true”、“false”、“yes”或“no”。

例如:

<add 
   name="AppEntities" 
   connectionString="
      metadata=res://*/App_Code.AppModel.csdl|res://*/App_Code.AppModel.ssdl|res://*/App_Code.AppModel.msl;
      provider=System.Data.SqlClient;
      provider connection string=&quot;
         Data Source=.;
         Initial Catalog=Example;
         Integrated Security=True;
         Pooling=False;
         MultipleActiveResultSets=True
      &quot;" 
   providerName="System.Data.EntityClient"
/>

解决方案 2

看起来可能存在涉及信号量的解决方法,详细信息如下:

具有多线程服务的数据库连接池

于 2013-06-24T20:11:52.733 回答
1

假设这是您在测试中运行的所有代码,您可能没有查看挂起的连接。这是连接池的正常行为。

当您使用单线程时,.net 可以重用相同的物理连接,因为它已经从上一个命令中关闭。对于多个线程,它必须打开几个物理连接来处理并行请求。

附言。您应该使用 using 语句而不是手动 .Dispose,因为如果打开后的内容引发异常,您可能会面临打开连接的时间比预期更长的风险。因此,您可能会用完连接。

于 2013-06-26T23:47:45.013 回答