使用线程池而不是异步IO的HttpClient.SendAsync?
所以我一直在通过Reflector挖掘HttpClient.SendAsync
的实现。 我有意想知道的是这些方法的执行流程,以及确定调用哪个API来执行异步IO工作。
在探索了HttpClient
的各种类之后,我在内部看到它使用HttpClientHandler
,它派生自HttpMessageHandler
并实现其SendAsync
方法。
这是HttpClientHandler.SendAsync
的实现:
protected internal override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { if (request == null) { throw new ArgumentNullException("request", SR.net_http_handler_norequest); } this.CheckDisposed(); this.SetOperationStarted(); TaskCompletionSource source = new TaskCompletionSource(); 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
并返回由它创建的Task
执行请求。
为什么我觉得这很奇怪? 好吧,我们继续讨论I / O绑定异步操作如何在幕后不需要额外的线程,以及它是如何重叠IO的。
为什么使用Task.Factory.StartNew
来触发异步I / O操作? 这意味着SendAsync
不仅使用纯异步控制流来执行此方法,而且还在“我们背后”旋转ThreadPool线程来执行其工作。
this.startRequest
是一个指向this.startRequest
的委托,后者又使用HttpWebRequest.BeginGetResponse
来启动异步IO。 HttpClient
在封面下使用异步IO,只包含在线程池任务中。
也就是说,请注意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);
这解决了HttpWebRequest的一个众所周知的问题:它的一些处理阶段是同步的。 这是该API的一个缺陷。 HttpClient
通过将DNS工作移动到线程池来避免阻塞。
是好还是坏? 它很好,因为它使HttpClient
非阻塞并适合在UI中使用。 这很糟糕,因为我们现在使用线程进行长时间运行的阻塞工作,尽管我们预计根本不会使用线程。 这降低了使用异步IO的好处。
实际上,这是混合同步和异步IO的一个很好的例子。 使用两者并没有任何内在错误。 HttpClient
和HttpWebRequest
正在使用异步IO进行长时间运行的阻塞工作(HTTP请求)。 他们使用线程进行短期工作(DNS,…)。 总的来说,这不是一个糟糕的模式。 我们避免了大多数阻塞,我们只需要将代码的一小部分作为异步。 典型的80-20权衡。 在BCL(库)中找到这样的东西并不好,但在应用程序级代码中可以是一个非常聪明的权衡。
似乎最好修复HttpWebRequest
。 出于兼容性原因,这可能是不可能的。
上述就是C#学习教程:使用线程池而不是异步IO的HttpClient.SendAsync?分享的全部内容,如果对大家有所用处且需要了解更多关于C#学习教程,希望大家多多关注—计算机技术网(www.ctvol.com)!
本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。
ctvol管理联系方式QQ:251552304
本文章地址:https://www.ctvol.com/cdevelopment/1029134.html