实体框架线程连接管理

4
在我的多线程Windows服务中,我在每个线程上打开与数据库的连接,然后释放这些连接,但问题是当我在sys.sysprocesses表上执行查询时,有一些连接没有关闭。
我运行了两个单元测试并观察到一些奇怪的行为。在第一个测试中,我运行了带有100个任务的循环,在每个任务中都会打开一个新的连接。在所有任务完成后(通过WaitAll()观察),我发现有些连接仍然挂在数据库中。而在第二个单元测试中,当我进行多个打开/释放操作而不是并行执行时,它们被正常释放了,我的数据库中也没有挂起的连接。
问题在于,在我的Windows服务中,这些挂起的连接会累加,并且最终没有更多的空间提供给用户,导致无法使用数据库。
以下是代码,第一个是并行的,第二个则不是:
    [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();
    }

谢谢


3
这不是EF或TPL的问题,而是由于连接池导致的。 - Aron
2个回答

4
使用连接池技术,每个工作进程(在本例中是线程)都有自己的连接池。如果将最大池大小设置为3,并生成100个线程,则可能会有300个连接。

以下是一位遇到类似问题的人提供的更多信息:

ODP.NET连接池参数

还有一些来自微软的文档,介绍了连接池如何工作以及连接池碎片化的解释:

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

看起来有一个解决方法,涉及到信号量,在这里详细介绍:

使用多线程服务进行数据库连接池化


1
假设这是您在测试中运行的所有代码,那么您可能没有考虑到挂起的连接。这是使用连接池时的正常行为。
当您使用单个线程时,.net可以重用已经关闭的物理连接,因为它来自上一个命令。但是,在使用多个线程时,它必须打开多个物理连接以处理并行请求。
附:您应该使用using语句而不是手动的.Dispose,因为如果打开后面的东西抛出异常,您会冒着连接打开时间超过预期的风险。从而导致无法连接。

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