简体   繁体   English

来自线程的实体框架连接管理

[英]Entity Framework connection management from threads

In my multi threaded windows service I open connections to the db on each thread, after that I dispose those connections, though the problem is that some are left not closed on the db when I perform query on the sys.sysprocesses table. 在我的多线程Windows服务中,我在每个线程上打开与数据库的连接,之后我处理了这些连接,但问题是当我在sys.sysprocesses表上执行查询时,某些数据库没有在数据库上关闭。

I ran two unit tests and saw some weird behavior, in first I ran a loop with 100 tasks, and in everyone of them I open a new connection. 我运行了两个单元测试并看到了一些奇怪的行为,首先我运行了一个包含100个任务的循环,并在每个人中打开一个新连接。 After these are finished (I see via WaitAll()) I see that some connections are still hanging in the db. 完成这些后(我通过WaitAll()看到)我看到一些连接仍然挂在数据库中。 On second unit test, when I run several open/dispose without parallel execution, it disposes them just fine and there are no hanging connections in my db. 在第二次单元测试中,当我在没有并行执行的情况下运行多个open / dispose时,它将它们处理得很好并且我的数据库中没有挂起的连接。

The problem is that in my Windows Service, these hanging connections are being added up and in the end there is no more place for new connections and db becomes not usable for the user. 问题是,在我的Windows服务中,这些挂起的连接正在被添加,最终没有新连接的地方,并且数据库变得不可用于用户。

Here are the codes, first is parallel, second is not: 这是代码,第一个是并行,第二个不是:

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

Thanks 谢谢

With connection pooling, each worker process (or in this case thread) has it's own connection pool. 使用连接池,每个工作进程(或者在本例中为线程)都有自己的连接池。 So if you have Max Pool Size set to say 3, and spawn 100x threads, you potentially can have 300 connections. 因此,如果您将Max Pool Size设置为3,并生成100x线程,则可能有300个连接。

More information from someone who had a similar problem: 来自有类似问题的人的更多信息:

ODP.NET Connection Pooling Parameters ODP.NET连接池参数

And some documentation from MS about how connection pools work, and an explantion of connection pool fragmentation: 以及MS提供的有关连接池如何工作的一些文档,以及连接池碎片的说明:

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

Solution 1 解决方案1

Turn connection pooling off in the connection string 在连接字符串中关闭连接池

MSDN - Connection String - Pooling MSDN - 连接字符串 - 池

When the value of this key is set to true, any newly created connection will be added to the pool when closed by the application. 当此键的值设置为true时,任何新创建的连接将在应用程序关闭时添加到池中。 In a next attempt to open the same connection, that connection will be drawn from the pool. 在下次尝试打开同一连接时,将从池中提取该连接。 Connections are considered the same if they have the same connection string. 如果连接具有相同的连接字符串,则认为连接相同。 Different connections have different connection strings. 不同的连接具有不同的连接串。

The value of this key can be "true", "false", "yes", or "no". 该键的值可以是“true”,“false”,“yes”或“no”。

For example: 例如:

<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"
/>

Solution 2 解决方案2

It looks like there may be a workaround involving semaphores, detailed here: 看起来可能有一个涉及信号量的解决方法,详情如下:

Database connection pooling with multi-threaded service 使用多线程服务的数据库连接池

Assuming that is all the code you are running in the test, you are probably not looking at hanged connections. 假设这是您在测试中运行的所有代码,您可能不会看到被挂起的连接。 This is normal behavior with connection pooling. 这是连接池的正常行为。

When you use a single thread .net can reuse the same physical connection as it was already closed from the previous command. 当您使用单个线程时,.net可以重用与先前命令已关闭的相同的物理连接。 With multiple threads, it has to open several physical connections to attend to the parallel requests. 对于多个线程,它必须打开几个物理连接以处理并行请求。

Ps. PS。 You should use the using statement instead of the manual .Dispose, as you risk having connections opened longer than expected if the stuff after the open throws an exception. 您应该使用using语句而不是手动.Dispose,因为如果打开后的内容抛出异常,您可能会打开比预期更长的连接。 And thus you can run out of connections. 因此,您可以用完连接。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM