取消静态HttpClient上的异步调用

时间:2017-11-10 16:25:43

标签: c# task-parallel-library scalability dotnet-httpclient cancellation

我正在使用静态HttpClient(出于可扩展性原因 - 请参阅What is the overhead of creating a new HttpClient per call in a WebAPI client?),并希望能够取消花费太长时间的个别请求。 SendAsync上的重载需要CancellationToken - 但我不知道它是否是线程安全的,因为我的HttpClient实例是static。例如,如果我同时通过HttpClient发送了多个请求,并尝试取消一个请求,是否取消了正确的请求?

我通过HttpClient代码看了一下,乍看起来它看起来不是线程安全的,因为取消发送到HttpClientHandler(这对所有人来说都是一样的)要求)。但我可能会遗漏一些东西。所以我的问题是:

  1. 我可以取消静态HttpClient上的个别请求吗?
  2. 如果没有,我该如何做到这一点?
  3. 注意: 自测试以来,需要一种方法可靠创建竞争条件,在我无法控制的代码中,我没有看到测试方法。

2 个答案:

答案 0 :(得分:4)

每个SendAsync调用完全相互独立,取消一个请求的令牌不会取消其他未完成的请求。

您的假设是因为所有请求共享HttpClientHandler,这意味着所有请求都被取消,这是不正确的。如果你查看HttpClientHandler的反编译来源,你会看到

[__DynamicallyInvokable]
protected internal override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
  if (request == null)
    throw new ArgumentNullException(nameof (request), SR.net_http_handler_norequest);
  this.CheckDisposed();
  if (Logging.On)
    Logging.Enter(Logging.Http, (object) this, nameof (SendAsync), (object) request);
  this.SetOperationStarted();
  TaskCompletionSource<HttpResponseMessage> completionSource = new TaskCompletionSource<HttpResponseMessage>();
  HttpClientHandler.RequestState state = new HttpClientHandler.RequestState();
  state.tcs = completionSource;
  state.cancellationToken = cancellationToken;
  state.requestMessage = request;
  try
  {
    HttpWebRequest prepareWebRequest = this.CreateAndPrepareWebRequest(request);
    state.webRequest = prepareWebRequest;
    cancellationToken.Register(HttpClientHandler.onCancel, (object) prepareWebRequest);
    if (ExecutionContext.IsFlowSuppressed())
    {
      IWebProxy webProxy = (IWebProxy) null;
      if (this.useProxy)
        webProxy = this.proxy ?? WebRequest.DefaultWebProxy;
      if (this.UseDefaultCredentials || this.Credentials != null || webProxy != null && webProxy.Credentials != null)
        this.SafeCaptureIdenity(state);
    }
    Task.Factory.StartNew(this.startRequest, (object) state);
  }
  catch (Exception ex)
  {
    this.HandleAsyncException(state, ex);
  }
  if (Logging.On)
    Logging.Exit(Logging.Http, (object) this, nameof (SendAsync), (object) completionSource.Task);
  return completionSource.Task;
}

每次调用HttpClientHandler.RequestState state时,取消令牌都会被包含在新的SendAsnyc对象中,当取消该令牌时,只有与该状态对象关联的state.webRequest才会被取消被取消。

答案 1 :(得分:0)

刚收到Microsoft产品团队的确认:

  

是的,使用支持取消个人请求是完全安全的   取消令牌传递到各种HttpClient.SendAsync,   。GetAsync等方法。 HttpClient是无关紧要的   &#34; static&#34 ;.传递给方法的取消令牌用于   那个特殊的要求。