![](/img/trans.png)
[英]Using WebProxy with HttpClient.SendAsync() and HttpRequestMessage
[英]HttpClient.SendAsync using the thread-pool instead of async IO?
所以我一直在通過Reflector挖掘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綁定異步操作如何在幕后不需要額外的線程,以及它是如何重疊IO的。
為什么使用Task.Factory.StartNew
來觸發異步I / O操作? 這意味着SendAsync
不僅使用純異步控制流來執行此方法,而且還在“我們背后”旋轉ThreadPool線程來執行其工作。
this.startRequest
是指向一個代表到StartRequest
又使用HttpWebRequest.BeginGetResponse
開始異步IO。 HttpClient
在封面下使用異步IO,只包含在線程池任務中。
// 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
。 由於兼容性原因,這可能是不可能的。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.