首页 文章

取消静态HttpClient上的异步调用

提问于
浏览
2

我正在使用静态HttpClient(出于可伸缩性的原因 - 请参阅What is the overhead of creating a new HttpClient per call in a WebAPI client?),并希望能够取消花费太长时间的单个请求 . SendAsync 上有一个需要 CancellationToken 的重载 - 但由于我的 HttpClient 实例是 static ,所以我没有't know if it'的线程安全 . 例如,如果我同时通过 HttpClient 发送了多个请求,并且我尝试取消一个请求,是否取消了正确的请求?

我通过 HttpClient 代码看了一下,乍一看它似乎不是线程安全的,因为取消被发送到 HttpClientHandler (对于所有请求都是一样的) . 但我可能会遗漏一些东西 . 所以我的问题是:

  • 我可以取消静态HttpClient上的个别请求吗?

  • 如果没有,我该怎么做到这一点?

注意:由于测试这个,需要一种方法 reliably 创建竞争条件, in code that I do not control ,我没有看到测试方法 .

2 回答

  • 0

    每个 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;
    }
    

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

  • 4

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

    是的,使用传递到各种HttpClient.SendAsync,.GetAsync等方法的取消令牌取消单个请求是完全安全的 . HttpClient是“静态的”并不重要 . 传递给方法的取消令牌仅用于该特定请求 .

相关问题