HttpClient的异步任务管理

时间:2013-12-12 22:34:28

标签: c# task-parallel-library task async-await

我正在创建一个通用加载器,我想开始HttpClient SendAsync请求。但是,其中一些请求可能需要一些时间,因此我想添加取消选项,并在完成时通知。

这似乎是一个标准的场景imho。

我不确定这是否是正确的解决方法,但根据我看过的一些例子,这就是我所处的位置。如果你查看代码的底部,我的问题是 - 那时,我是否检查响应并引发成功或错误事件

    public bool StartFetch()
    {
        if (IsFetching) return false;
        IsFetching = true;

        mCancellationTokenSource = new CancellationTokenSource();

        // this is not awaited, so execution should continue
        StartTask(request, mCancellationTokenSource.Token);
        return true;
    }

    public bool CancelFetch()
    {
        // send cancellation
        if (mCancellationTokenSource != null)
            mCancellationTokenSource.Cancel();

        Cleanup();
        return true;
    }


    private async Task StartTask(LFHttpRequest request, CancellationToken cancellationToken)
    {
        var message = new HttpRequestMessage(request.Method, request.Uri);
        var response = await HttpClient.SendAsync(message, cancellationToken); 

        // at this point, do I take a look at response and raise a custom OnSuccess or OnError event???   

       // or do I want to grab the task from `SendAsync`, check for completed or faulted?
    }

3 个答案:

答案 0 :(得分:3)

如果您正在考虑公开与IsFetching等任务相关的状态,那么公开Task本身通常会更简洁,更容易。

这样的事情:

public Task<T> FetchTask { get; private set; }

public bool StartFetch()
{
    if (FetchTask != null) return false;

    mCancellationTokenSource = new CancellationTokenSource();
    FetchTask = FetchAsync(request, mCancellationTokenSource.Token);
    return true;
}

public bool CancelFetch()
{
    // send cancellation
    if (mCancellationTokenSource != null)
        mCancellationTokenSource.Cancel();

    FetchTask = null;
    return true;
}


private async Task<T> FetchAsync(LFHttpRequest request, CancellationToken cancellationToken)
{
    var message = new HttpRequestMessage(request.Method, request.Uri);
    var response = await HttpClient.SendAsync(message, cancellationToken); 
    response.EnsureSuccessStatusCode();
    var ret = // Convert response.Content into T.
    return ret;
}

答案 1 :(得分:0)

如果InvalidOperationException状态无效,我建议您为StartFetchCancelFetch操作投放IsFetching秒。这可能看起来很烦人,但它可以让你在程序员错误和线程问题成为一个更大的隐藏问题之前捕获它们。

对于异步方法,您的方法应返回结果。也许像private async Task<MyHttpResult> StartTask(...)这样的东西。您的结果应包含确定成功,失败和取消的方式。

例如:

public sealed class MyHttpResult
{
  public HttpResponse Result { get; private set; }
  public Exception Error { get; private set; }
  public bool WasCancelled { get; private set; }

  public MyHttpResult(HttpResponse result, Exception error, bool wasCancelled)
  {
    this.Result = result;
    this.Error = error;
    this.WasCancelled = wasCancelled;
  }
}

如果许多异步方法被取消,它们会抛出TaskCanceledException,因此你可以catch表示,如下所示:

async Task<MyHttpResult> StartTask(LFHttpRequest request, CancellationToken cancellationToken)
{
  var message = new HttpRequestMessage(new HttpMethod(request.Method), request.Uri);

  HttpResponse response = null;
  Exception lastError = null;
  bool wasCancelled = false;

  try
  {
    response = await MessageInvoker.SendAsync(message, cancellationToken);
  }
  catch(TaskCanceledException)
  {
    wasCancelled = true;
  }
  catch(Exception ex)
  {
    lastError = ex;
  }

  var result = new MyHttpResult(response, lastError, wasCancelled);
  return result;
}

这是假设你的观察者也是调用者,所以他们可以await这种方法。如果不是这种情况,那么您对EventHandler的看法就有意义了。您可以创建一个自定义EventArgs类,而不是返回结果:

public delegate void TaskResultEventHandler<T>(object sender, TaskResultEventArgs<T> e);

public sealed class TaskResultEventArgs<T> : EventArgs
{
      public T Result { get; private set; }
      public Exception Error { get; private set; }
      public bool WasCancelled { get; private set; }

      public TaskResultEventArgs(T result, Exception error, bool wasCancelled)
      {
        this.Result = result;
        this.Error = error;
        this.WasCancelled = wasCancelled;
      }
}

然后,这只是暴露TaskResultEventHandler<HttpResponse>和你的观察者订阅它的问题。你可以像这样调用它:

var handler = this.HttpTaskCompleted;

if(handler != null)
  handler(this, new TaskResultEventArgs<HttpResponse>(response, lastError, wasCancelled));

答案 2 :(得分:0)

等待http呼叫后

var response = await HttpClient.SendAsync(message, cancellationToken); 

您应该测试取消:

if(cancellationToken.IsCancellationRequested)
   //... do what you want, throw or return false or null, depending on how you want to handle this cancellation.

或者您可以在一次通话中检查并抛出Microsoft异常:

cancel.ThrowIfCancellationRequested();