Сведения о вопросе

Fhohir

05:43, 20th August, 2020

Теги

.net   multithreading    

Нить не просыпается от Thread.Sleep()

Просмотров: 437   Ответов: 8

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

Это работает нормально в течение нескольких месяцев, но недавно мы видели несколько случаев, когда оператор log.Error() регистрирует исключение "System.InvalidOperationException: This SqlTransaction has completed; it is no longer usable", а затем никогда не возвращается. Служба может быть оставлена работать в течение нескольких дней, но больше ничего не будет зарегистрировано.

Сделав некоторое чтение, я знаю, что Thread.Sleep не идеален,но почему он просто никогда не вернется?



  Сведения об ответе

SSESION

02:18, 8th August, 2020

Покопаться и выяснить? Воткни отладчик в этого ублюдка!

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

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

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

  • Sleep() висит.

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


  Сведения об ответе

Chhiki

20:26, 8th August, 2020

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

Я думаю, что лучшим вариантом было бы сделать так, чтобы ваша система ведения журнала ловила дубликаты, чтобы она могла написать что-то вроде: "The previous message was repeated N times".

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

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


  Сведения об ответе

baggs

14:40, 22nd August, 2020

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

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

Прерывание потоков, по-видимому, не работает в контексте обработчика исключений.


  Сведения об ответе

PHPH

19:09, 4th August, 2020

Наткнулся на это, когда искал свою собственную проблему Thread.Sleep. Это может быть связано или не связано, но если ваш doSomething() создает исключение, closeDatabaseConnections() не произойдет, что может привести к утечке ресурсов.. Я бы поместил это в последний блок. Просто есть о чем подумать.


  Сведения об ответе

baggs

10:29, 4th August, 2020

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


  Сведения об ответе

VCe znayu

16:20, 16th August, 2020

Из кода, который вы опубликовали, не ясно, что после выброса исключения система определенно может перезапуститься - например, если исключение происходит из doStuff(), то поток управления вернется (после 10-минутного ожидания) в openConnection(), никогда не проходя через closeConnection().

Но, как уже говорили другие, просто подключите отладчик и найдите, где он на самом деле находится.


  Сведения об ответе

$DOLLAR

16:31, 19th August, 2020

Попробуй Thread.Sleep(10 * 60 * 1000)


  Сведения об ответе

ASSembler

19:29, 27th August, 2020

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


Ответить на вопрос

Чтобы ответить на вопрос вам нужно войти в систему или зарегистрироваться