2010-02-09 154 views
12

我注意到这个问题在我做的大多数事情中都发生了很多事情,所以我一直在想这样的设计模式。尝试,捕捉问题

基本上,如果抛出异常,试图解决问题并重试。如果我把它放在try中,它所要做的就是捕获异常,但我想重试它正在做的任何事情,如果再次失败,再次重试一定次数。

这种东西是否有共同的模式?

+1

条件循环? 如果你没有处理异常,你为什么把它放在try/catch – davidosomething 2010-02-09 17:58:23

+5

疯狂是两次做同样的事情,并期待不同的结果。我说,如果你遇到异常,几乎总是要做的最好的事情是告诉用户并让他们解决问题。 – 2010-02-09 18:10:39

+3

@Eric我猜他正在做一些类似于运行连接到另一项服务的服务。如果连接失败,则抛出异常。他想重试连接,因为可能有许多原因与他自己的失败代码(服务器故障,网络故障,路由故障等)无关。通知用户有点愚蠢,因为用户只会做同样的事情,试图重新连接。 – 2010-02-09 18:26:27

回答

17

检查这个苏答案..希望帮助ü

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

呼叫

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

幸得LBushkin

+3

+1:这个获得我的赞赏。这是代表活着的东西。 – 2010-02-09 18:06:50

+1

我正要对第二个答案作出回应,并说:“好吧,我只是将它抽象为一个需要委托并添加重试计数器的方法。”然后我向下滚动。优秀的答案。 – 2010-02-09 18:14:31

+0

-1:即使只发生'numRetries'次,毯子捕捉也没有意义。你不知道抛出了哪个异常,或者重试是否有意义。 – 2010-02-10 19:38:17

2

try/catch在一个循环内,有一个计数器用于重试?你需要自定义逻辑,如何重试变化很大(例如,重新打开一个流,重新创建对象,暂停X毫秒等等......)编辑:你需要“重试它在做什么” ),所以你需要在每个原子操作的循环内部有自己的try/catch。

“原子操作”我的意思是一组相关的语句,比如读取一个文件。例如,读入内存的整个文件可能是一个原子操作。

2

在一些有限的基础上,您可能希望将try/catch放入循环中,并在最终成功时强制中断。这可能是因特网接入测试,你希望用户再次尝试连接。

6

这无限期地运行,但它会很容易做广告d一个循环计数器,用于while子句

var solved = false; 
    var tries = 0; 

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

       tries++; 
     } 
    } 
+1

这将永远循环! – Dolph 2010-02-09 17:59:35

+1

@Dolph Mathews:当然!如果起初你不成功... – 2010-02-09 18:01:10

+0

是的,它会永远循环直到它达到解决=真; – Pauk 2010-02-09 18:02:33

1

是的,通常会有一个带有多次重试的循环,您可以在成功时跳出循环。几件事情:

您可能希望在重试之前添加一个延迟,以便在临时问题有时间修复之前的几毫秒内不用完所有重试。

如果你最终失败了,你应该抛出第一个例外,而不是最后一个。第二个异常可能是第一次失败后无法正确恢复的结果,可能无助于调试原始问题。

2

事情是这样的,也许:

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

     throw; 
    } 
} 
+1

马克在他的回答中提出了一个很好的观点。根据情况,如果超过最大重试次数,可能需要存储对原始异常的引用,然后重新引发它(或者只是抛出最后一个异常,将原始异常传递为InnerException)。另外,正如Mark所说,如果预期的异常可能由资源争用引起,您可能希望在重试之间添加延迟。 – 2010-02-09 18:00:43

1

取决于你正在尝试,但通常要检查一个例外PRIOR发生在执行可能导致异常的代码的可能性。

例如,在访问文件之前检查文件是否存在,如果不存在,请创建它(或其他)。

+0

如果文件不存在,或者文件无法访问,则File.Exists可以返回false。除非您尝试打开文件,否则您永远不会知道它们中的哪一个。这样,你也会得到一个异常,详细说明为什么不能打开文件(至少,如果你使用ex.ToString()),你会。 – 2010-02-10 19:40:04

1

您确定异常处理是否适合您吗?如果您可以“解决问题”,则可能在调用异常生成代码之前检测错误情况。

异常处理对于非常特殊的事情来说是最自然的。互联网连接失败(如前面的答案)是在之前可以检测并处理调用异常抛出代码。

+0

除非在您尝试检测到并在使用之前互联网连接可能开始失败。 – 2010-02-11 13:18:37

1

编码别人已经说过:

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

这是一个更好的解决方案,因为您正在尝试。但是,用户应该了解什么是异常,并尝试阻止它或捕获该异常。 +1的尝试。 – 2010-02-09 18:04:49

+0

@Mathews:C#中没有'Integer'。它是'int'或'Int32'。 – missingfaktor 2010-02-09 18:07:45

+0

我是用Java编写的,随时可以编辑到C# – Dolph 2010-02-09 18:07:46