моя проблема :Ранее на этой неделе я получил задачу ускорить задачу в нашей программе. Я посмотрел на это и сразу же понял, что можно использовать параллельный цикл 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 -, еще больше ускорило процесс.
Особая благодарность Марку Гравеллу за то, что он все объяснил и остался со мной в субботу;)