如果发生某些错误,则在后台重新启动任务

时间:2014-01-24 04:41:08

标签: c# multithreading task-parallel-library

我正在使用一些使用Mono.Mac(3.2.3)的REST请求与服务器通信,作为重试机制,我正在悄悄地试图在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()首次完成(以及后续失败)后再次运行。有没有一种简单的方法可以做到这一点,还是我以这种方式滥用任务?是否有某种方法可以将任务重置为初始状态,或者我最好使用某种工厂?

2 个答案:

答案 0 :(得分:4)

由于以下几个原因,你确实在滥用Task

  • 您不能多次运行同一任务。完成后,就完成了。

  • 建议不要手动构建Task对象,其中Task.RunTask.Factory.Start

  • 您不应将Task.Run / Task.Factory.Start用于执行IO绑定工作的任务。它们用于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

答案 1 :(得分: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()
{
  ...
}

此代码来自Microsoft的重试模式设计。您可以在此处查看:https://msdn.microsoft.com/en-us/library/dn589788.aspx