Управление подключением Entity Framework из потоков

В моей многопоточной службе Windows я открываю подключения к базе данных в каждом потоке, после чего удаляю эти подключения, хотя проблема в том, что некоторые из них остаются незакрытыми в базе данных, когда я выполняю запрос в таблице sys.sysprocesses.

Я провел два модульных теста и увидел какое-то странное поведение. Сначала я запустил цикл со 100 задачами, и в каждом из них я открываю новое соединение. После того, как они закончены (я вижу через WaitAll ()), я вижу, что некоторые соединения все еще зависают в базе данных. Во втором модульном тесте, когда я запускаю несколько open / dispose без параллельного выполнения, он удаляет их просто отлично, и в моем db нет зависших соединений.

Проблема в том, что в моей службе 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();
    }

Спасибо


person Sagi    schedule 02.06.2013    source источник
comment
Это не вопрос EF или TPL. Это связано с пулом подключений   -  person Aron    schedule 23.06.2013


Ответы (2)


При использовании пула соединений каждый рабочий процесс (или в данном случае поток) имеет собственный пул соединений. Таким образом, если у вас установлен максимальный размер пула, равный 3, и создается 100 потоков, у вас потенциально может быть 300 подключений.

Дополнительная информация от человека, у которого была аналогичная проблема:

Параметры пула подключений ODP.NET

И некоторая документация от MS о том, как работают пулы соединений, и объяснение фрагментации пула соединений:

MSDN - пул подключений SQL Server (ADO.NET)

Решение 1

Отключить пул подключений в строке подключения

MSDN - Строка подключения - Объединение < / strong>

Если для этого ключа установлено значение true, любое вновь созданное соединение будет добавлено в пул при закрытии приложением. При следующей попытке открыть то же соединение оно будет извлечено из пула. Подключения считаются одинаковыми, если они имеют одинаковую строку подключения. У разных подключений разные строки подключения.

Значение этого ключа может быть «истина», «ложь», «да» или «нет».

Например:

<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

Похоже, что может быть обходной путь с использованием семафоров, подробно описанный здесь:

Объединение соединений с базой данных с помощью многопоточной службы

person Tom Halladay    schedule 24.06.2013

Предполагая, что это весь код, который вы запускаете в тесте, вы, вероятно, не смотрите на зависшие соединения. Это нормальное поведение с пулом соединений.

Когда вы используете один поток .net может повторно использовать то же физическое соединение, которое уже было закрыто предыдущей командой. С несколькими потоками он должен открыть несколько физических соединений для обработки параллельных запросов.

Пс. Вы должны использовать оператор using вместо manual .Dispose, так как вы рискуете, что соединения будут открываться дольше, чем ожидалось, если материал после открытия выдает исключение. И поэтому у вас могут закончиться соединения.

person eglasius    schedule 26.06.2013