У нас есть служба Windows, написанная на C#. Сервис порождает поток, который делает следующее:
private void ThreadWorkerFunction()
{
while(false == _stop) // stop flag set by other thread
{
try
{
openConnection();
doStuff();
closeConnection();
}
catch (Exception ex)
{
log.Error("Something went wrong.", ex);
Thread.Sleep(TimeSpan.FromMinutes(10));
}
}
}
Мы добавили Thread.Sleep через пару раз, когда база данных исчезла, и мы вернулись к файлам журналов 3 Гбайт, заполненным ошибками подключения к базе данных.
Это работало нормально в течение нескольких месяцев, но недавно мы видели несколько случаев, когда оператор log.Error () регистрирует исключение «System.InvalidOperationException: этот SqlTransaction завершен; он больше не может использоваться», а затем никогда не возвращается . Службу можно оставить работающей на несколько дней, но больше ничего не будет регистрироваться.
Прочитав немного, я знаю, что Thread.Sleep не идеален, но почему он просто никогда не вернется?





Вы пробовали использовать Монитор. Импульс (убедитесь, что ваш поток использует управление потоками, прежде чем запускать это), чтобы заставить поток что-то делать? Если это сработает, вам придется немного подробнее изучить логику потоковой передачи.
We put the Thread.Sleep in after a couple of times when the database had gone away and we came back to 3Gb logs files full of database connection errors.
Я бы подумал, что лучшим вариантом было бы сделать так, чтобы ваша система журналирования улавливала дубликаты, чтобы она могла писать что-то вроде «Предыдущее сообщение было повторено N раз».
Предположим, я написал стандартную заметку о том, как вы должны открывать свое соединение в последний возможный момент и закрывать его при первой же возможности, вместо того, чтобы охватывать потенциально огромную функцию так, как вы это сделали (но, возможно, это артефакт. вашего демонстрационного кода, и ваше приложение действительно написано правильно).
Когда вы говорите, что он сообщает об ошибке, которую вы описываете, вы имеете в виду, что этот обработчик сообщает об ошибке? Причина, по которой мне это непонятно, заключается в том, что во фрагменте кода вы говорите «Что-то пошло не так», но вы не сказали этого в своем описании; Я бы не хотел, чтобы это было чем-то настолько глупым, поскольку исключение перехватывается где-то еще, а код застревает где-то, кроме сна.
Копайся и узнаешь? Воткни этому ублюдку отладчик!
Я вижу по крайней мере следующие возможности:
И, возможно, но почти наверняка нет, следующее:
Но в любом случае присоединение отладчика покажет вам, существует ли еще поток и действительно ли он завис.
Из опубликованного вами кода неясно, что после создания исключения система определенно может перезапуститься - например, если исключение исходит от doStuff (), тогда поток управления вернется (после 10-минутного ожидания) к openConnection (), никогда не проходя через closeConnection ().
Но, как говорили другие, просто подключите отладчик и найдите, где он на самом деле.
Попробуйте Thread.Sleep (10 * 60 * 1000)
Я так и не понял, что происходит, но, похоже, это было связано с исключениями ThreadInterruptedExceptions, которые были выброшены во время 10-минутного сна, поэтому я изменил код на:
private void ThreadWorkerFunction()
{
DateTime? timeout = null;
while (!_stop)
{
try
{
if (timeout == null || timeout < DateTime.Now)
{
openDatabaseConnections();
doStuff();
closeDatabaseConnections();
}
else
{
Thread.Sleep(1000);
}
}
catch (ThreadInterruptedException tiex)
{
log.Error("The worker thread was interrupted... ignoring.", tiex);
}
catch (Exception ex)
{
log.Error("Something went wrong.", ex);
timeout = DateTime.Now + TimeSpan.FromMinutes(10);
}
}
}
Помимо конкретного перехвата ThreadInterruptedException, это просто кажется более безопасным, поскольку весь сон происходит в блоке try, поэтому все неожиданные события будут регистрироваться. Я обновлю этот ответ, если когда-нибудь узнаю больше.
Находится ли ваш рабочий поток в пуле потоков или это настоящий поток?
Наткнулся на это, когда искал собственную проблему Thread.Sleep. Это может быть связано или нет, но если ваш doSomething () выдает исключение, closeDatabaseConnections () не произойдет, что может привести к утечке ресурсов ... Я бы поместил это в блок finally. Просто кое что для раздумий.
У меня была точно такая же проблема. Перемещение строки сна за пределы обработчика исключений устранило проблему для меня, например:
bool hadError = false;
try {
...
} catch (...) {
hadError = true;
}
if (hadError)
Thread.Sleep(...);
Кажется, что прерывание потоков не работает в контексте обработчика исключений.