SqlConnections, Parallel.For, старое приложение C #и случайные зависания при инициализации SqlConnections

моя проблема :Ранее на этой неделе я получил задачу ускорить задачу в нашей программе. Я посмотрел на это и сразу же понял, что можно использовать параллельный цикл foreach для функции в этой задаче.

Я реализовал его, прошел через функцию (, включая все вспомогательные -функции ), и изменил SqlConnections (и другие вещи ), чтобы они могли работать параллельно. Я начал все это, и все прошло хорошо и быстро (в одиночку, что сократило время для этой задачи на ~45%)

Итак, вчера мы хотели попробовать то же самое с некоторыми другими данными и... У меня возникла странная проблема :Всякий раз, когда вызывалась параллельная функция, она работала... но иногда один из потоков зависал на минимум 4 минуты (тайм-ауты установлены на одну минуту,для соединения И команды ).

Если я приостановлю программу во время этого, я увижу, что из этого цикла все еще активен только один поток, и он зависает на

connection.Open()

Через ~4 минуты программа просто продолжает работу, не выдавая ошибку (, за исключением сообщения в поле «Вывод», говорящего о том, что где-то произошло исключение, но оно было перехвачено не моим приложением, а где-то в объекте SqlConnection/SqlCommand. ).

Я могу убить все соединения на MSSQLServer, и ничего не происходит, также MSSQLServer ничего не делает в течение этих 4 минут, все соединения простаивают.

Это процедура, которая используется для отправки операторов обновления/вставки/удаления в базу данных :

int i = 80;
bool itDidntWork = true;
Random random = new Random();
while (itDidntWork && i > 0)
{
    try
    {
        using (SqlConnection connection = new SqlConnection(sqlConnectionString))
        {
            connection.Open();
            lock (connection)
            {
                command.Connection = connection;
                command.ExecuteNonQuery();
            }

            itDidntWork = false;
        }
    }
    catch (Exception ex)
    {
        if (ex is SqlException && ((SqlException)ex).ErrorCode == -2146232060)
        {
            Thread.Sleep(random.Next(500, 5000));
        }
        else
        {
            SqlConnection.ClearAllPools();
        }

        Thread.Sleep(random.Next(50, 110));
        i--;
        if (i == 0)
        {
            writeError(ex);
        }
    }
}

. на всякий случай :в небольших базах данных могут возникнуть взаимоблокировки (номер ошибки 2146232060 ), поэтому, если это произойдет, я должен заставить операторы конфликтов возникать в разное время. Отлично работает даже на небольших базах данных/небольших серверах. Если ошибка не была вызвана взаимоблокировкой, скорее всего, соединение было ошибочным, поэтому я очищаю все разорванные соединения.

Аналогичные функции существуют для выполнения скаляров, заполнения таблиц/наборов данных (да, это приложение это старое )и выполнение хранимых процедур.

И да, все они используются в параллельном цикле.

Кто-нибудь знает, что там может происходить? Или идея о том, как я могу узнать, что там происходит?

*изменить объект команды:

он передается функции, объект команды всегда является новым объектом, когда он передается функции.

о блокировке :Если я убираю блокировку, я получаю десятки и сотни ошибок «соединение закрыто» или «соединение уже открыто», потому что функция «Открыть» ()просто получает соединение из пула соединений.NET. Замок работает как положено.

Пример кода:

using(SqlCommand deleteCommand = new SqlCommand(sqlStatement))
{
    ExecuteNonQuerySafely(deleteCommand); // that's the function that contains the body I posted above
}

*редактировать 2

Я должен внести поправку :Он висит на этом

command.Connection = connection;

по крайней мере, я думаю, что это так,потому что, когда я приостанавливаю приложение, значок «шаг» становится зеленым и горит

command.ExecuteNonQuery();

говоря, что это оператор, который будет выполнен следующим.

*редактировать 3 просто чтобы убедиться, что я только что начал еще один тест без каких-либо блокировок вокруг объекта подключения... потребуется несколько минут, чтобы получить результаты.

*редактировать 4 ну, я был неправ. Я удалил операторы блокировки и... он все еще работал. Может быть, в первый раз, когда я попробовал это, было повторно использованное соединение или что-то в этом роде. Спасибо, что указали на это.

*редактировать 5 У меня такое ощущение, что это происходит только с одним конкретным вызовом определенной процедуры базы данных. Не знаю почему. C #нет разницы между этим звонком и другими звонкамисм. правку 6 . И поскольку он не выполнил оператор в тот момент (, я думаю. Может быть, кто-то может поправить меня в этом. Если в режиме отладки строка помечена зеленым цветом (вместо желтого ), она еще не выполнила этот оператор, но ждет его до завершения этой строки, правильно ли это? )это странно.

*редактировать 6 Было 3 командных объекта, которые использовались повторно все время. Они были определены выше параллельной функции. Я не знаю, насколько это плохо/было. Они использовались только для вызова одной хранимой процедуры (, каждая из которых вызывала другую процедуру ), разумеется, с другими параметрами и новым соединением (с помощью вышеупомянутого метода ).

*редактировать 7 хорошо, это действительно только тогда, когда вызывается одна конкретная хранимая процедура. За исключением того, что на назначении объекта соединения он висит (следующая строка помечена зеленым ). Попытка выяснить, в чем причина этого atm.

*изменить 8 да, это только что произошло в другой команде. Так что это было.

*изменить 9 в порядке. Проблема решена. «Зависания» на самом деле были CommandTimeouts, которые были установлены на 10 минут (! ).Они были установлены только для двух команд (, той, которую я упомянул в редактировании 7, и той, которую я упомянул в редактировании 8 ). Поскольку я нашел их обоих, когда реструктурировал свои команды, чтобы сделать их такими, как предложил devundef, я отметил его ответ как тот, который решил мою проблему. Кроме того, его предложение ограничить количество потоков, используемых моим циклом for -, еще больше ускорило процесс.

Особая благодарность Марку Гравеллу за то, что он все объяснил и остался со мной в субботу;)

5
задан 15 revs, 4 users 83% 17 October 2015 в 06:32
поделиться