Pergunta

Temos um serviço do Windows escrito em C#.O serviço gera um thread que faz isso:

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));
    }
  }
}

Colocamos o Thread.Sleep depois de algumas vezes, quando o banco de dados desapareceu e voltamos aos arquivos de log de 3 Gb cheios de erros de conexão do banco de dados.

Isso tem funcionado bem há meses, mas recentemente vimos alguns casos em que a instrução log.Error() registra uma "System.InvalidOperationException:Este SqlTransaction foi concluído;não é mais utilizável" e nunca mais volta.O serviço pode permanecer em execução por dias, mas nada mais será registrado.

Depois de ler um pouco, sei que Thread.Sleep não é ideal, mas por que simplesmente nunca mais voltaria?

Foi útil?

Solução

Aprofunde-se e descubra?Coloque um depurador naquele bastardo!

Posso ver pelo menos as seguintes possibilidades:

  1. o sistema de registro trava;
  2. o thread saiu bem, mas o serviço ainda está em execução porque alguma outra parte tem um erro lógico.

E talvez, mas quase certamente não, o seguinte:

  • Sleep() trava.

Mas, em qualquer caso, anexar um depurador mostrará se o thread ainda está lá e se realmente travou.

Outras dicas

Colocamos o Thread.Sleep depois de algumas vezes, quando o banco de dados desapareceu e voltamos aos arquivos de log de 3 Gb cheios de erros de conexão do banco de dados.

Eu acho que uma opção melhor seria fazer com que seu sistema de registro capturasse duplicatas, para que pudesse escrever algo como "A mensagem anterior foi repetida N vezes".

Suponha que eu escrevi uma nota padrão sobre como você deve abrir sua conexão no último momento possível e fechá-la na primeira oportunidade, em vez de abranger uma função potencialmente enorme da maneira como você a fez (mas talvez isso seja um artefato do seu código demonstrativo e seu aplicativo está realmente escrito corretamente).

Quando você diz que está relatando o erro que você descreve, você quer dizer que este manipulador está relatando o erro?A razão pela qual não está claro para mim é que no trecho de código você diz "Algo deu errado", mas não disse isso em sua descrição;Eu não gostaria que isso fosse algo tão bobo, já que a exceção está sendo capturada em outro lugar e o código está preso em outro lugar que não seja o sono.

Eu tive exatamente o mesmo problema.Mover a linha Sleep para fora do manipulador de exceções corrigiu o problema para mim, assim:

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

A interrupção de threads parece não funcionar no contexto de um manipulador de exceções.

Você já tentou usar Monitor.Pulso (certifique-se de que seu thread esteja usando o gerenciamento de threads antes de executar isso) para fazer com que o thread faça alguma coisa?Se isso funcionar, você terá que examinar um pouco mais sua lógica de threading.

A partir do código que você postou, não está claro se, após uma exceção ser lançada, o sistema será definitivamente capaz de reiniciar - por exemplo.se a exceção vier de doStuff(), então o fluxo de controle retornará (após a espera de 10 minutos) para openConnection(), sem nunca passar por closeConnection().

Mas, como já foi dito, basta anexar um depurador e descobrir onde ele realmente está.

Experimente Thread.Sleep(10 * 60 * 1000)

Eu nunca descobri completamente o que estava acontecendo, mas parecia estar relacionado ao fato de ThreadInterruptedExceptions serem lançadas durante o sono de 10 minutos, então mudei o código para:

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);
    }
  }
}

Além de capturar especificamente ThreadInterruptedException, isso parece mais seguro, pois todo o sono acontece dentro de um bloco try, portanto, qualquer coisa inesperada que acontecer será registrada.Atualizarei esta resposta se descobrir mais.

Tropecei nisso enquanto procurava um problema do Thread.Sleep.Isso pode ou não estar relacionado, mas se doSomething() lançar uma exceção, closeDatabaseConnections() não acontecerá, o que tem algum potencial para vazamentos de recursos.Eu colocaria isso em um bloco final.Apenas algo para se pensar.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top