2014-01-24 43 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

相关问题