Вопрос

У нас есть служба 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 не идеален, но почему он просто никогда не вернется?

Это было полезно?

Решение

Покопайтесь и узнаете?Воткните этому ублюдку отладчик!

Я вижу как минимум следующие возможности:

  1. система журналирования зависает;
  2. поток завершился нормально, но служба все еще работает, поскольку в какой-то другой части возникла логическая ошибка.

И возможно, но почти наверняка нет, следующее:

  • Sleep() зависает.

Но в любом случае подключение отладчика покажет, существует ли еще тред и действительно ли он завис.

Другие советы

Мы подключили Thread.Sleep через пару раз, когда база данных исчезла, и мы вернулись к файлам журналов размером 3 ГБ, полным ошибок подключения к базе данных.

Я думаю, что лучшим вариантом было бы сделать так, чтобы ваша система журналирования ловила дубликаты и могла писать что-то вроде: «Предыдущее сообщение было повторено N раз».

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

Когда вы говорите, что он сообщает об ошибке, которую вы описываете, вы имеете в виду, что этот обработчик сообщает об ошибке?Причина, по которой мне это непонятно, заключается в том, что во фрагменте кода вы говорите: «Что-то пошло не так», но вы не сказали этого в своем описании;Я бы не хотел, чтобы это было настолько глупо, поскольку исключение перехватывается где-то еще, и код зависает где-то кроме сна.

У меня была точно такая же проблема.Перемещение строки Sleep за пределы обработчика исключений устранило проблему, например:

bool hadError = false;
try {
  ...
} catch (...) {
  hadError = true;
}
if (hadError)
  Thread.Sleep(...);

Прерывание потоков, похоже, не работает в контексте обработчика исключений.

Вы пробовали использовать Монитор.Пульс (перед запуском убедитесь, что ваш поток использует управление потоками), чтобы заставить поток что-то сделать?Если это сработает, вам придется немного больше изучить логику потоков.

Из опубликованного вами кода неясно, что после создания исключения система определенно сможет перезагрузиться - например.если исключение исходит из 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.Просто кое что для раздумий.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top