Catch块中的重试?

12

我该如何实现catch块中的代码?

  try
    {
       // Call a MS SQL stored procedure (MS SQL 2000)
       // Stored Procedure may deadlock 
    }
    catch
    {
       // if deadlocked Call a MS SQL stored procedure (may deadlock again)
       // If deadlocked, keep trying until stored procedure executes
    }
    finally
    {

    }

如果你进入了catch块,你想要重新执行try块吗? - Tudor
7个回答

26

这样做并不被推荐,并且可能会在你的程序中引发严重问题。例如,如果数据库宕机了怎么办?

但是,以下是如何在循环中执行此操作的方法:

for(int attempts = 0; attempts < 5; attempts++)
// if you really want to keep going until it works, use   for(;;)
{
    try
    {
        DoWork();
        break;
    }
    catch { }
    Thread.Sleep(50); // Possibly a good idea to pause here, explanation below
}
更新: 正如下面Mr. Disappointment在评论中提到的: Thread.Sleep 方法会暂停执行指定数量的毫秒。没有错误是完全随机的,大多数只需要再试一次就可以工作是因为在尝试之间所花费的时间内发生了某些变化。暂停线程的执行将为这种情况提供更大的机会窗口(例如,数据库引擎启动的时间更长)。

+1 对于“这样做不被推荐且可能会导致严重问题”的陈述。 - Fischermaen
如果你确实要这样做,建议在下一次尝试之前进行一段“休眠”时间 - 例如,在引擎启动时检查数据库,会导致五次尝试在它有机会之前迅速通过。 - Grant Thomas
一个很好的观点。我会在答案中加上原因。谢谢。 - Connell
如果数据库宕机,故障将在到达此特定try块之前发生。喜欢有限次尝试和重试之前的暂停的想法。谢谢。 - AAsk
这解决了我使用另一个库时遇到的问题,如果该编解码器恰好同时被使用,则会抛出随机编解码器错误,但是没有办法在不进行大量编码的情况下检查编解码器。第一次重试90%成功,第二次100%成功。 - user2924019

5
这个怎么样?
bool retry = true;
while( retry ){
  try{
    ...
    retry = false;
  }
  catch
  {
    ...
  }
  finally
  {
    ...
  }
}

只要try块的最后一行被执行(retry = false),它将继续往下执行。如果出现异常,它会运行catch和finally块,然后回到顶部重新运行try块。
如果您只想尝试x次,请使用int替换retry,并以尝试次数为初始值。然后在while循环中检查它是否等于0,在循环开始时将其递减,并将其设置为0作为try块的最后一行。
当然,您应该对空的catch块进行处理,以捕获您预期的异常,而不是捕获所有异常。

1
这是错误的逻辑,因为如果它继续失败,你可能会陷入无限循环中。在使用未知计数循环时,始终使用定时器或计时器。 - user1853517

3

以下内容摘自微软开发者网络页面,介绍了他们所谓的“重试模式”(Retry Pattern):

private int retryCount = 3;
...

public async Task OperationWithBasicRetryAsync()
{
  int currentRetry = 0;

  for (; ;)
  {
    try
    {
      // Calling external service.
      await TransientOperationAsync();

      // Return or break.
      break;
    }
    catch (Exception ex)
    {
      Trace.TraceError("Operation Exception");

      currentRetry++;

      // Check if the exception thrown was a transient exception
      // based on the logic in the error detection strategy.
      // Determine whether to retry the operation, as well as how 
      // long to wait, based on the retry strategy.
      if (currentRetry > this.retryCount || !IsTransient(ex))
      {
        // If this is not a transient error 
        // or we should not retry re-throw the exception. 
        throw;
      }
    }

    // Wait to retry the operation.
    // Consider calculating an exponential delay here and 
    // using a strategy best suited for the operation and fault.
    Await.Task.Delay();
  }
}

// Async method that wraps a call to a remote service (details not shown).
private async Task TransientOperationAsync()
{
  ...
}

他们详细解释了此模式的适当使用和不适当使用。例如,如果您预计遇到的错误是短暂的,并且稍后重试可能会成功,那么这可能适合您。如果这是为了帮助您处理一些扩展问题,那么这并不适合您。

您还可能对他们的断路器模式感兴趣,他们将其描述为能够“处理连接到远程服务或资源时可能需要不同时间来纠正的故障。”


2
可能只需要将整个try/catch包装在一个while循环中:
while (!success) {

    try
    {
       // Call a MS SQL stored procedure (MS SQL 2000)
       // Stored Procedure may deadlock 
       success = true;
    }
    catch
    {
       // if deadlocked Call a MS SQL stored procedure (may deadlock again)
       // If deadlocked, keep trying until stored procedure executes
       success = false;
    }

}

这也是错误的逻辑,因为如果它继续失败,你可能会陷入无限循环中。在使用未知计数循环时,始终使用定时器或计时器。 - user1853517

2
你真的不应该只是一直尝试执行你的存储过程直到它成功,但这是另一回事。
你可以这样做:
Boolean succeeded = false;

while (!succeeded)
{

    try
    {
        // Call a MS SQL stored procedure (MS SQL 2000)
        // Stored Procedure may deadlock 
        succeeded = true;
    }
    catch (Exception ex)
    {
        // Log
    }
}

1
这段代码只会运行一次,对吗?无论是否抛出异常,finally块都会被调用,并将succeeded设置为true,导致while循环中止。 - Øyvind Bråthen

1
请勿在catch块中实现它。相反,编写一个循环,直到成功或达到某个限制为止重复执行。

类似于:

bool quit = false;
int loopcount = 0;
while(!quit )
{
   try
   {
       // execute the command, might throw an exception)
       quit = true; // no exception if you got here
   }
   catch(Exception ex)
   {
      if (ex != deadlock) // doesn't work like this :-(
        quit = true;
   }
   finally
   {
      // etc.
   }
   loopcount++;
   if (loopcount > 3)
      quit = true;
}

0
您可以实现定时器来检查存储过程的健康状况,并在循环内根据此抛出答案,就像同事们所说的那样。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接