Question

Nous avons un service Windows écrit en C#.Le service génère un thread qui fait ceci :

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

Nous avons mis Thread.Sleep après plusieurs fois, lorsque la base de données avait disparu et nous sommes revenus à des fichiers journaux de 3 Go remplis d'erreurs de connexion à la base de données.

Cela fonctionne bien depuis des mois, mais récemment, nous avons vu quelques cas où l'instruction log.Error() enregistre une "System.InvalidOperationException :Cette SqlTransaction est terminée ;il n'est plus utilisable" exception et ne revient jamais.Le service peut rester opérationnel pendant plusieurs jours, mais rien de plus ne sera enregistré.

Après avoir lu quelques lectures, je sais que Thread.Sleep n'est pas idéal, mais pourquoi ne reviendrait-il tout simplement jamais ?

Était-ce utile?

La solution

Creusez et découvrez ?Collez un débogueur sur ce salaud !

Je peux voir au moins les possibilités suivantes :

  1. le système de journalisation se bloque ;
  2. le thread s'est bien terminé mais le service est toujours en cours d'exécution car une autre partie présente une erreur logique.

Et peut-être, mais presque certainement pas, ce qui suit :

  • Sleep() se bloque.

Mais dans tous les cas, attacher un débogueur vous montrera si le thread est toujours là et s'il est réellement bloqué.

Autres conseils

Nous avons mis Thread.Sleep après plusieurs fois, lorsque la base de données avait disparu et nous sommes revenus à des fichiers journaux de 3 Go remplis d'erreurs de connexion à la base de données.

Je pense qu'une meilleure option serait de faire en sorte que votre système de journalisation piège les doublons, afin qu'il puisse écrire quelque chose comme "Le message précédent a été répété N fois".

Supposons que j'ai écrit une note standard sur la façon dont vous devez ouvrir votre connexion au dernier moment possible et la fermer le plus tôt possible, plutôt que de couvrir une fonction potentiellement énorme de la manière dont vous l'avez fait (mais c'est peut-être un artefact) de votre code démonstratif et votre application est effectivement rédigée correctement).

Quand vous dites qu'il signale l'erreur que vous décrivez, voulez-vous dire que ce gestionnaire est-ce que signaler l'erreur ?La raison pour laquelle cela n'est pas clair pour moi est que dans l'extrait de code, vous dites "Quelque chose s'est mal passé", mais vous ne l'avez pas dit dans votre description ;Je ne voudrais pas que ce soit quelque chose d'aussi stupide que l'exception est interceptée ailleurs et que le code reste bloqué ailleurs que dans le sommeil.

J'ai eu exactement le même problème.Déplacer la ligne Sleep en dehors du gestionnaire d'exceptions a résolu le problème pour moi, comme ceci :

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

L'interruption des threads ne semble pas fonctionner dans le contexte d'un gestionnaire d'exceptions.

Avez-vous essayé d'utiliser Moniteur.Pulse (assurez-vous que votre thread utilise la gestion des threads avant de l'exécuter) pour que le thread fasse quelque chose ?Si cela fonctionne, vous devrez alors examiner un peu plus votre logique de thread.

D'après le code que vous avez publié, il n'est pas clair qu'après la levée d'une exception, le système soit définitivement capable de redémarrer - par ex.si l'exception vient de doStuff(), alors le flux de contrôle reviendra (après les 10 minutes d'attente) à openConnection(), sans jamais passer par closeConnection().

Mais comme d'autres l'ont dit, attachez simplement un débogueur et trouvez où il se trouve réellement.

Essayez Thread.Sleep (10 * 60 * 1000)

Je n'ai jamais vraiment compris ce qui se passait, mais cela semblait être lié au fait que des ThreadInterruptedExceptions étaient levées pendant les 10 minutes de sommeil, j'ai donc changé le code pour :

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

En plus d'attraper spécifiquement l'exception ThreadInterruptedException, cela semble tout simplement plus sûr car toute la mise en veille se produit dans un bloc try, donc tout ce qui se produit d'inattendu sera enregistré.Je mettrai à jour cette réponse si jamais j'en saurai plus.

Je suis tombé sur cela en cherchant mon propre problème Thread.Sleep.Cela peut être lié ou non, mais si votre doSomething() lève une exception, closeDatabaseConnections() ne se produira pas, ce qui présente un certain potentiel de fuite de ressources.Je mettrais ça dans un bloc final.Juste quelque chose à penser.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top