2014-03-25 258 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. 
} 
相關問題