2014-01-24 22 views
3

我正在使用一些REST請求使用Mono.Mac(3.2.3)與服務器進行通信,並且作爲重試機制,我正在悄悄地嘗試爲HTTP操作提供多次嘗試如果他們失敗或超時。如果發生某些錯誤,在後臺重新啓動任務

我有以下;

var tries = 0; 
while (tries <= ALLOWED_TRIES) 
{ 
    try 
    { 
     postTask.Start(); 
     tries++; 
     if (!postTask.Wait(Timeout)) 
     { 
      throw new TimeoutException("Operation timed out"); 
     } 
     break; 
    } catch (Exception e) { 
     if (tries > ALLOWED_TRIES) 
     { 
      throw new Exception("Failed to access Resource.", e); 
     } 
    } 
} 

其中任務使用父方法的參數如此;

var postTask = new Task<HttpWebResponse>(() => {return someStuff(foo, bar);}, 
    Task.Factory.CancellationToken, 
    Task.Factory.CreationOptions); 

的問題似乎是,任務不希望這是第一次完成(以及隨後的失敗)後,與postTask.Start()再次運行。有沒有這樣做的簡單方法,還是我以這種方式濫用任務?有什麼方法可以將任務重置爲初始狀態,還是我最好使用某種工廠?

回答

3

你的確在濫用Task這裏,有幾個原因:

  • 您不能運行相同的任務不止一次。完成後,就完成了。

  • 不建議手動構建Task對象,這裏有Task.RunTask.Factory.Start

  • 對於執行IO綁定工作的任務,您不應該使用Task.Run/Task.Factory.Start。它們用於CPU綁定的工作,因爲它們從ThreadPool「借用」線程以執行任務操作。相反,使用純異步Task爲基礎的API,這不需要一個專門的線程來完成。

例如,下面你可以調用從UI線程GetResponseWithRetryAsync,仍然保持UI響應:

async Task<HttpWebResponse> GetResponseWithRetryAsync(string url, int retries) 
{ 
    if (retries < 0) 
     throw new ArgumentOutOfRangeException(); 

    var request = WebRequest.Create(url); 
    while (true) 
    { 
     try 
     { 
      var result = await request.GetResponseAsync(); 
      return (HttpWebResponse)result; 
     } 
     catch (Exception ex) 
     { 
      if (--retries == 0) 
       throw; // rethrow last error 
      // otherwise, log the error and retry 
      Debug.Print("Retrying after error: " + ex.Message); 
     } 
    } 
} 

更多閱讀:

"Task.Factory.StartNew" vs "new Task(...).Start"

Task.Run vs Task.Factory.StartNew

+0

這個假定C#5.0,從這個問題我不清楚這是一個選項。 – svick

+0

@svick,使用'ContinueWith'回調也可以做到這一點,但代碼不會那麼可讀。 – Noseratio

+1

@svick,這是一個[.NET 4.0版本](http://stackoverflow.com/a/21346870/1768303),作爲一個學習練習,我學會了更多地愛上「異步/等待」。 – Noseratio

0

我建議做這樣的事情:

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() 
{ 
    ... 
} 

此代碼是從微軟的重試圖案設計。你可以在這裏查看:https://msdn.microsoft.com/en-us/library/dn589788.aspx

相關問題