gpt4 book ai didi

c# - HttpClient.SendAsync 使用线程池而不是异步 IO?

转载 作者:可可西里 更新时间:2023-11-01 09:05:13 32 4
gpt4 key购买 nike

所以我一直在深入研究 HttpClient.SendAsync 的实现通过反射器。我有意想知道这些方法的执行流程,并确定调用哪个 API 来执行异步 IO 工作。

探索里面的各个类之后HttpClient ,我看到它在内部使用 HttpClientHandler源自 HttpMessageHandler并实现其 SendAsync方法。

这是 HttpClientHandler.SendAsync 的实现:

protected internal override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
if (request == null)
{
throw new ArgumentNullException("request", SR.net_http_handler_norequest);
}

this.CheckDisposed();
this.SetOperationStarted();

TaskCompletionSource<HttpResponseMessage> source = new TaskCompletionSource<HttpResponseMessage>();

RequestState state = new RequestState
{
tcs = source,
cancellationToken = cancellationToken,
requestMessage = request
};

try
{
HttpWebRequest request2 = this.CreateAndPrepareWebRequest(request);
state.webRequest = request2;
cancellationToken.Register(onCancel, request2);

if (ExecutionContext.IsFlowSuppressed())
{
IWebProxy proxy = null;

if (this.useProxy)
{
proxy = this.proxy ?? WebRequest.DefaultWebProxy;
}
if ((this.UseDefaultCredentials || (this.Credentials != null)) || ((proxy != null) && (proxy.Credentials != null)))
{
this.SafeCaptureIdenity(state);
}
}

Task.Factory.StartNew(this.startRequest, state);
}
catch (Exception exception)
{
this.HandleAsyncException(state, exception);
}
return source.Task;
}

我发现奇怪的是上面使用了Task.Factory.StartNew在生成 TaskCompletionSource<HttpResponseMessage> 的同时执行请求并返回 Task由它创建。

为什么我觉得这很奇怪?好吧,我们继续讨论 I/O 绑定(bind)异步操作如何在幕后不需要额外的线程,以及它是如何与重叠 IO 相关的。

为什么要使用 Task.Factory.StartNew触发异步 I/O 操作?这意味着SendAsync不仅使用纯异步控制流来执行此方法,而且 “在我们背后” 旋转 ThreadPool 线程来执行其工作。

最佳答案

this.startRequest 是一个指向 StartRequest 的委托(delegate),后者又使用 HttpWebRequest.BeginGetResponse 来启动异步 IO。 HttpClient 在幕后使用异步 IO,只是包装在线程池任务中。

也就是说,请注意以下内容 comment in SendAsync

// BeginGetResponse/BeginGetRequestStream have a lot of setup work to do before becoming async
// (proxy, dns, connection pooling, etc). Run these on a separate thread.
// Do not provide a cancellation token; if this helper task could be canceled before starting then
// nobody would complete the tcs.
Task.Factory.StartNew(startRequest, state);

This works around a well-known problem with HttpWebRequest: Some of its processing stages are synchronous.这是该 API 中的一个缺陷。 HttpClient 通过将 DNS 工作移至线程池来避免阻塞。

这是好事还是坏事?它很好,因为它使 HttpClient 成为非阻塞的并且适合在 UI 中使用。这很糟糕,因为我们现在正在使用一个线程来进行长时间运行的阻塞工作,尽管我们预计根本不使用线程。这会降低使用异步 IO 的好处。

实际上,这是混契约(Contract)步和异步 IO 的一个很好的例子。使用两者本质上没有错。 HttpClientHttpWebRequest 使用异步 IO 进行长时间运行的阻塞工作(HTTP 请求)。他们将线程用于短期运行的工作(DNS,...)。一般来说,这不是一个坏模式。我们正在避免大多数 阻塞,我们只需要使代码的一小部分异步。典型的 80-20 权衡。在 BCL(一个库)中找到这样的东西并不好,但在应用程序级代码中可以是一个非常明智的权衡。

看来最好修复 HttpWebRequest。出于兼容性原因,这可能是不可能的。

关于c# - HttpClient.SendAsync 使用线程池而不是异步 IO?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24983635/

32 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com