Pregunta

Tenemos un servicio de Windows escrito en C#.El servicio genera un hilo que hace esto:

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 Thread.Sleep después de un par de veces cuando la base de datos desapareció y volvimos a archivos de registros de 3 Gb llenos de errores de conexión a la base de datos.

Esto ha estado funcionando bien durante meses, pero recientemente hemos visto algunos casos en los que la declaración log.Error() registra una "System.InvalidOperationException:Esta SqlTransaction se ha completado;ya no es utilizable" excepción y luego nunca vuelve.El servicio se puede dejar funcionando durante días pero no se registrará nada más.

Después de leer un poco, sé que Thread.Sleep no es ideal, pero ¿por qué simplemente nunca volvería?

¿Fue útil?

Solución

¿Profundizar y descubrirlo?¡Ponle un depurador a ese bastardo!

Puedo ver al menos las siguientes posibilidades:

  1. el sistema de registro se cuelga;
  2. el hilo salió bien pero el servicio aún se está ejecutando porque alguna otra parte tiene un error lógico.

Y tal vez, pero casi con certeza no, lo siguiente:

  • Dormir() se bloquea.

Pero en cualquier caso, adjuntar un depurador le mostrará si el hilo todavía está ahí y si realmente se ha colgado.

Otros consejos

Colocamos Thread.Sleep después de un par de veces cuando la base de datos desapareció y volvimos a archivos de registros de 3 Gb llenos de errores de conexión a la base de datos.

Creo que una mejor opción sería hacer que su sistema de registro capture duplicados, de modo que pueda escribir algo como "El mensaje anterior se repitió N veces".

Supongamos que he escrito una nota estándar sobre cómo debes abrir tu conexión en el último momento posible y cerrarla lo antes posible, en lugar de abarcar una función potencialmente enorme en la forma en que lo has hecho (pero tal vez eso sea un artefacto). de su código demostrativo y su aplicación está realmente escrita correctamente).

Cuando dices que está informando el error que describe, ¿quieres decir que este manejador está reportando el error?La razón por la que no me queda claro es que en el fragmento de código dices "Algo salió mal", pero no lo dijiste en tu descripción;No quisiera que esto fuera algo tan tonto como que la excepción se detecta en otro lugar y el código se atasca en otro lugar que no sea el modo de suspensión.

He tenido exactamente el mismo problema.Mover la línea Sleep fuera del controlador de excepciones me solucionó el problema, así:

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

La interrupción de subprocesos no parece funcionar en el contexto de un controlador de excepciones.

¿Has intentado usar Monitor.Pulso (asegúrese de que su hilo esté usando la administración de hilos antes de ejecutar esto) para que el hilo haga algo.Si eso funciona, entonces tendrás que analizar un poco más tu lógica de subprocesos.

Según el código que ha publicado, no está claro que después de que se produce una excepción, el sistema definitivamente pueda reiniciarse, por ejemplo.Si la excepción proviene de doStuff(), entonces el flujo de control volverá (después de la espera de 10 minutos) a openConnection(), sin pasar nunca por closeConnection().

Pero como han dicho otros, simplemente conecte un depurador y encuentre dónde está realmente.

Pruebe Thread.Sleep(10 * 60 * 1000)

Nunca descubrí completamente lo que estaba pasando, pero parecía estar relacionado con el lanzamiento de ThreadInterruptedExceptions durante los 10 minutos de suspensión, así que cambié el código a:

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

Además de detectar específicamente ThreadInterruptedException, esto se siente más seguro ya que todo el modo de dormir ocurre dentro de un bloque de prueba, por lo que cualquier cosa inesperada que suceda se registrará.Actualizaré esta respuesta si alguna vez descubro más.

Me topé con esto mientras buscaba mi propio problema de Thread.Sleep.Esto puede estar relacionado o no, pero si doSomething() genera una excepción, closeDatabaseConnections() no sucederá, lo que tiene cierto potencial para fugas de recursos.Lo pondría en un bloque finalmente.Solo algo para pensar.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top