2014-03-25 170 views
4

目前我们有天真RetryWrapper其在异常发生重试定FUNC:在C#中实现Retry Wrapper的最佳方式是什么?

public T Repeat<T, TException>(Func<T> work, TimeSpan retryInterval, int maxExecutionCount = 3) where TException : Exception 
{ 
    ... 

而对于retryInterval我们使用下面的逻辑下一次尝试之前“等待”。

_stopwatch.Start(); 
while (_stopwatch.Elapsed <= retryInterval) 
{ 
    // do nothing but actuallky it does! lots of CPU usage specially if retryInterval is high 
} 
_stopwatch.Reset(); 

我并不特别喜欢这个逻辑,也非常我宁愿重试逻辑不是主线程上发生的,你能想到更好的办法?

注:我很高兴地考虑答案为.NET> = 3.5

+0

可能重复[如何等待一段时间或函数调用,无论采取最长的系统时间更改,即使?](http://stackoverflow.com/questions/5107522/how-to-等待一段时间或函数调用 - 取最长 - 甚至) – CodeCaster

回答

3

只要你的方法签名返回一个T,主线程将不得不阻塞,直到所有重试完成。但是,您可以通过让线程睡眠,而不是做一个手动重置事件减少CPU:

Thread.Sleep(retryInterval); 

如果你愿意改变你的API,你可以把它,这样你就不会阻塞主线程。例如,你可以使用异步方法:

public async Task<T> RepeatAsync<T, TException>(Func<T> work, TimeSpan retryInterval, int maxExecutionCount = 3) where TException : Exception 
{ 
    for (var i = 0; i < maxExecutionCount; ++i) 
    { 
     try { return work(); } 
     catch (TException ex) 
     { 
      // allow the program to continue in this case 
     } 
     // this will use a system timer under the hood, so no thread is consumed while 
     // waiting 
     await Task.Delay(retryInterval); 
    } 
} 

这能够同步消耗:

RepeatAsync<T, TException>(work, retryInterval).Result; 

但是,您也可以启动任务,然后等待它以后:

var task = RepeatAsync<T, TException>(work, retryInterval); 

// do other work here 

// later, if you need the result, just do 
var result = task.Result; 
// or, if the current method is async: 
var result = await task; 

// alternatively, you could just schedule some code to run asynchronously 
// when the task finishes: 
task.ContinueWith(t => { 
    if (t.IsFaulted) { /* log t.Exception */ } 
    else { /* success case */ } 
}); 
0

如何使用计时器而不是秒表?

例如:

TimeSpan retryInterval = new TimeSpan(0, 0, 5); 
    DateTime startTime; 
    DateTime retryTime; 
    Timer checkInterval = new Timer(); 

    private void waitMethod() 
    { 
     checkInterval.Interval = 1000; 
     checkInterval.Tick += checkInterval_Tick;   
     startTime = DateTime.Now; 
     retryTime = startTime + retryInterval; 
     checkInterval.Start(); 
    } 

    void checkInterval_Tick(object sender, EventArgs e) 
    { 
     if (DateTime.Now >= retryTime) 
     { 
      checkInterval.Stop(); 

      // Retry Interval Elapsed 
     } 
    } 
+1

这仍然是一个旋转等待,并且在阻塞主线程时将消耗大量的CPU。另外,既然你保持重新分配startTime,这将永远运行。 – ChaseMedallion

+0

显然它需要在一个函数,这只是psudo代码 –

+1

@Okuma是的,它是伪代码,但即使这个伪代码将永远运行,因为'startTime <= startTime + retryInterval'总是'true '当retryInterval不等于零时,这不是。 – Maarten

2

考虑使用Transient Fault Handling Application Block

微软企业库瞬时故障处理应用 模块使开发人员能够使加入 强大的瞬时性故障处理逻辑及其应用更具弹性。瞬态故障是由于某些临时条件(如网络连接问题或服务不可用性)而发生的错误 。通常,如果您在短时间后重试 导致短暂错误的操作,您会发现该错误已消失。

它可作为NuGet包使用。

using Microsoft.Practices.TransientFaultHandling; 
using Microsoft.Practices.EnterpriseLibrary.WindowsAzure.TransientFaultHandling; 
... 
// Define your retry strategy: retry 5 times, starting 1 second apart 
// and adding 2 seconds to the interval each retry. 
var retryStrategy = new Incremental(5, TimeSpan.FromSeconds(1), 
    TimeSpan.FromSeconds(2)); 

// Define your retry policy using the retry strategy and the Windows Azure storage 
// transient fault detection strategy. 
var retryPolicy = 
    new RetryPolicy<StorageTransientErrorDetectionStrategy>(retryStrategy); 

// Receive notifications about retries. 
retryPolicy.Retrying += (sender, args) => 
    { 
     // Log details of the retry. 
     var msg = String.Format("Retry - Count:{0}, Delay:{1}, Exception:{2}", 
      args.CurrentRetryCount, args.Delay, args.LastException); 
     Trace.WriteLine(msg, "Information"); 
    }; 

try 
{ 
    // Do some work that may result in a transient fault. 
    retryPolicy.ExecuteAction(
    () => 
    { 
     // Your method goes here! 
    }); 
} 
catch (Exception) 
{ 
    // All the retries failed. 
} 
相关问题