2010-02-09 13 views
12

He notado que este problema ocurre mucho en la mayoría de las cosas que hago, así que estoy pensando que debe haber un patrón de diseño para esto.Probar, capturar el problema

Básicamente, si se produce una excepción, intente resolver el problema y vuelva a intentarlo. Si lo coloco en el intento, todo lo que hará es atrapar la excepción, pero quiero volver a intentar lo que sea que esté haciendo y, si falla nuevamente, volver a intentarlo una cierta cantidad de veces.

¿Hay un patrón común para este tipo de cosas?

+1

un bucle condicional? si no está manejando la excepción, ¿por qué lo está poniendo en un try/catch? – davidosomething

+5

Insanity está haciendo lo mismo dos veces y esperando resultados diferentes. Digo que si obtiene una excepción, casi siempre lo mejor es decirle al usuario y dejar que * ellos * resuelva el problema. –

+3

@Eric Supongo que está haciendo algo así como ejecutar un servicio que se conecta a otro servicio. Si la conexión falla, arroja una excepción. Quiere volver a intentar la conexión porque puede haber varias razones no asociadas con su propio código para la falla (servidor inactivo, red inactiva, error de enrutamiento, etc.). Notificar al usuario es algo tonto, ya que el usuario solo hará lo mismo e intentará volver a conectarse. –

Respuesta

17

cheque este SO responder .. espero que ayude u

Cleanest way to write retry logic?

public static class RetryUtility 
{ 
    public static void RetryAction(Action action, int numRetries, int retryTimeout) 
    { 
     if(action == null) 
      throw new ArgumenNullException("action"); 

     do 
     { 
      try 
      { 
       action(); 
       return; 
      } 
      catch 
      { 
       if(numRetries <= 0) 
        throw; // Avoid silent failure 
       else 
       { 
        Thread.Sleep(retryTimeout); 
        numRetries--; 
       } 
      } 
     } 
     while(numRetries > 0); 
    } 
} 

llamada

RetryUtility.RetryAction(() => SomeFunctionThatCanFail(), 3, 1000); 

El crédito va a LBushkin

+3

+1: Este obtiene mi voto popular. Es el tipo de cosa en la que viven los delegados. –

+1

Estaba a punto de responder a la segunda respuesta y decir: "Bueno, simplemente lo resumiría en un método que requiere un delegado y agregaría un contador de reintentos ...". Luego me desplacé hacia abajo. Excelente respuesta –

+0

-1: la captura de mantilla no tiene sentido, incluso si solo ocurre con el tiempo 'numRetries'. No tiene idea de qué excepción se lanzó, o si tiene sentido volver a intentarlo. –

2

try/catch dentro de un bucle, con un contador para los intentos?

EDITAR: Y su requisito de "volver a intentar lo que sea que esté haciendo", necesita lógica personalizada para eso, cómo volver a intentarlo varía enormemente (es decir, volver a abrir una secuencia, recrear el objeto, pausar X milisegundos, etc.), por lo que necesita su propio try/catch dentro de un bucle para cada operación atómica.

Por "operación atómica" me refiero a un conjunto de declaraciones relacionadas, como leer un archivo. El archivo completo leído en la memoria podría ser una operación atómica, por ejemplo.

2

De forma limitada, es posible que desee poner su try/catch en un bucle, y forzar el break si finalmente es exitoso. Tal podría ser para las pruebas de acceso a Internet y desea que el usuario tenga otro intento de conexión.

6

Esto ejecuta indefinidamente pero sería ser fácil de publicar d un contador de bucle a la cláusula mientras

var solved = false; 
    var tries = 0; 

    while (!solved) 
    { 
     try 
     { 
      //Do Something 
      solved = true; 
     } 
     catch 
     { 
      //Fix error 
     } 
     finally 
     { 
       if(solved || IsRediculous(tries)) 
       break; 

       tries++; 
     } 
    } 
+1

¡Eso funcionará para siempre! – Dolph

+1

@Dolph Mathews: ¡Por supuesto! Si al principio no tiene éxito ... –

+0

Sí, se repetirá siempre hasta que llegue a resolverse = verdadero; – Pauk

1

Sí, es bastante común tener un bucle con un número de reintentos en el que romper el bucle en el éxito. Un par de cosas:

Es posible que desee agregar un retraso antes de volver a intentarlo para que no agote todos sus intentos en unos pocos milisegundos antes de que el problema temporal haya tenido tiempo de arreglarse.

Si finalmente falla, debe lanzar la primera excepción que atrapó, no la última. La segunda excepción podría ser el resultado de no recuperarse correctamente de la primera falla y podría no ayudar a depurar el problema original.

2

Algo como esto, tal vez:

int MAX_RETRIES = 5; 
for (var attempt=1; attempt <= MAX_RETRIES; attempt++) { 
    try { 
     DoSomethingThatMightThrow(); 
    } 
    catch (AnExceptionIKnowHowToHandle) { 
     if (attempt < MAX_RETRIES) 
      continue; 

     throw; 
    } 
} 
+1

Mark hace un buen punto en su respuesta. Dependiendo de la situación, es posible que desee almacenar una referencia a la excepción original y luego volver a lanzarla (o simplemente lanzar la última excepción, pasando la original como InnerException) si supera los reintentos máximos.Además, como dijo Mark, es posible que desee agregar un retraso entre los reintentos si las excepciones esperadas podrían ser causadas por contención de recursos. –

1

Depende de lo que está tratando, pero por lo general se desea comprobar si hay la posibilidad de que ocurra una excepción antes de ejecutar el código que podría provocar una excepción.

Por ejemplo, compruebe que exista un archivo antes de acceder a él, y créelo (o lo que sea) si no lo hace.

+0

File.Exists puede devolver false si el archivo no existe o si no se puede acceder al archivo. Nunca sabrá cuál de los dos es a menos que intente abrir el archivo. De esta forma, también obtendrá una excepción con detalles sobre por qué no se puede abrir el archivo (al menos, lo hará si usa ex.ToString()). –

1

¿Estás seguro de que el manejo de excepciones es la metodología adecuada aquí?Si puede "resolver el problema", probablemente pueda detectar la condición de error antes de llamar al código de generación de excepciones.

El manejo de excepciones es más natural para cosas que son realmente excepcionales. Una conexión a Internet fallida (como en la respuesta anterior) es algo que se puede detectar y manejar antes de llamando al código de lanzamiento de excepción.

+0

Excepto que la conexión a Internet podría comenzar a fallar después de que haya intentado detectarlo y antes de usarlo. –

1

Codificación de lo que otros ya han mencionado:

var success = false; 
var attempts = 0; 
var maxAttempts = 0; 

do { 
    attempts++; 

    try { 
    /* your code */ 
    success = condition; 
    } catch(SuperciliousException e) { 
    /* recover */ 
    } 
} while(!success && attempts < maxAttempts); 
+0

Esta es una solución mejor, ya que está realizando intentos. Sin embargo, el usuario debe entender cuál es la excepción e intentar evitarlo o atrapar esa excepción. +1 para los intentos. –

+0

@Mathews: No hay 'Entero 'en C#. Es cualquiera 'int' o' Int32'. – missingfaktor

+0

Escribí esto en Java, siéntase libre de editar en C# – Dolph

Cuestiones relacionadas