С помощью HttpClient.SendAsync использует пул потоков вместо асинхронного ввода-вывода?
таким образом, я копался в реализации HttpClient.SendAsync
через отражатель. Я намеренно хотел узнать поток выполнения этих методов и определить, какой API вызывается для выполнения асинхронной работы ввода-вывода.
после изучения различных классов внутри 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
созданные им.
почему я нахожу это странным? ну, мы много говорим о том, как связанные асинхронные операции ввода-вывода не нуждаются в дополнительных потоках за кулисами и как все это связано с перекрывающимся IO.
почему это с помощью Task.Factory.StartNew
для запуска асинхронной операции ввода-вывода? это значит, что SendAsync
использует не только pure асинхронный поток управления для выполнения этого метода, но вращение потока ThreadPool "за нашей спиной" выполнить свою работу.
1 ответов
this.startRequest
- это делегат, который указывает на StartRequest
который в свою очередь использует HttpWebRequest.BeginGetResponse
для запуска асинхронного ввода-вывода. 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
неблокирующий и подходит для использования в пользовательском интерфейсе. Это плохо, потому что теперь мы используем поток для длительной работы блокировки, хотя мы ожидали, что не будем использовать потоки вообще. Это уменьшает преимущества использования асинхронного ввода-вывода.
на самом деле, это хороший пример смешения синхронизации и асинхронного ввода-вывода. Нет ничего плохого в использовании обоих. HttpClient
и HttpWebRequest
используют асинхронный ввод-вывод для длительная блокирующая работа (HTTP-запрос). Они используют потоки для краткосрочной работы (DNS, ...). В целом это неплохая схема. Мы избегаем большинство блокировка, и нам нужно только сделать небольшую часть кода асинхронным. Типичный компромисс 80-20. Нехорошо находить такие вещи в BCL (библиотеке), но в коде уровня приложения, который может быть очень умным компромиссом.
кажется, было бы желательно исправить HttpWebRequest
. Возможно, это не так. по причинам совместимости.