使用HttpClient.SendAsync线程池,而不是异步IO? [英] HttpClient.SendAsync using the thread-pool instead of async IO?

查看:2179
本文介绍了使用HttpClient.SendAsync线程池,而不是异步IO?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

所以我一直在通过反射 HttpClient.SendAsync 的实施挖掘。我故意想找出是这些方法的执行流程,并确定哪些API被调用执行异步IO的工作。

So I've been digging up on the implementation of HttpClient.SendAsync via Reflector. What I intentionally wanted to find out was the flow of execution of these methods, and to determine which API gets called to execute the asynchronous IO work.

探索在的HttpClient 的各种类后,我看到在内部它使用 HttpClientHandler 从<$ C派生$ C> HttpMessageHandler ,并实现它的 SendAsync 方法。

After exploring the various classes inside HttpClient, I saw that internally it uses HttpClientHandler which derives from HttpMessageHandler and implements its SendAsync method.

这是实施 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&LT; Htt的presponseMessage&GT ; 并返回工作由它创建

What I found weird is that the above uses Task.Factory.StartNew to execute the request while generating a TaskCompletionSource<HttpResponseMessage> and returning the Task created by it.

为什么我觉得这不可思议?好了,我们去了很多有关的I / O绑定异步操作怎么也没有必要在幕后额外的线程,以及如何将其所有关于重叠IO。

Why do I find this weird? well, we go on alot about how I/O bound async operations have no need for extra threads behind the scenes, and how its all about overlapped IO.

这是为什么使用 Task.Factory.StartNew 来触发一个异步I / O操作?这意味着 SendAsync 不仅采用纯异步控制流来执行这种方法,但纺线程池线程的我们的背后的执行它的工作。

Why is this using Task.Factory.StartNew to fire an async I/O operation? this means that SendAsync isn't only using pure async control flow to execute this method, but spinning a ThreadPool thread "behind our back" to execute its work.

推荐答案

this.startRequest 是指向 StartRequest 又使用 HttpWebRequest.BeginGetResponse 启动异步IO。 的HttpClient 在幕后采用异步IO,只是包裹在一个线程池的任务。

this.startRequest is a delegate that points to StartRequest which in turn uses HttpWebRequest.BeginGetResponse to start async IO. HttpClient is using async IO under the covers, just wrapped in a thread-pool Task.

这是说,请注意以下的comment在 SendAsync

That said, note the following 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);

这工作围绕一个公知的问题HttpWebRequest的:它的一些处理阶段是同步的。 这就是API中的一个缺陷。 的HttpClient 是避免通过移动DNS工作线程池阻塞。

This works around a well-known problem with HttpWebRequest: Some of its processing stages are synchronous. That is a flaw in that API. HttpClient is avoiding blocking by moving that DNS work to the thread-pool.

是好还是坏?这是件好事,因为它使的HttpClient 无阻塞,适合在一个界面中使用。这是因为我们现在使用一个线程长时间运行的阻塞工作虽然我们预计在所有不使用线程是坏的。这减少了使用异步IO的好处。

Is that good or bad? It is good because it makes HttpClient non-blocking and suitable for use in a UI. It is bad because we are now using a thread for long-running blocking work although we expected to not use threads at all. This reduces the benefits of using async IO.

其实,这是混合同步的一个很好的例子和异步IO。没有什么内在的错误同时使用。 的HttpClient 的HttpWebRequest 使用异步IO长时间运行的阻塞工作(HTTP请求)。他们正在使用线程短期运行工作(DNS,...)。这不是一般的坏的格局。我们避免的的拦网,我们只有让code异步的一小部分。一个典型的80-20权衡。这是不好的BCL(库),但在应用层面code,它可以是一个非常聪明的权衡找到这样的事情。

Actually, this is a nice example of mixing sync and async IO. There is nothing inherently wrong with using both. HttpClient and HttpWebRequest are using async IO for long-running blocking work (the HTTP request). They are using threads for short-running work (DNS, ...). That's not a bad pattern in general. We are avoiding most blocking and we only have to make a small part of the code async. A typical 80-20 trade-off. It is not good to find such things in the BCL (a library) but in application level code that can be a very smart trade-off.

看来这本来是preferable解决的HttpWebRequest 。也许这是不可能的兼容性原因。

It seems it would have been preferable to fix HttpWebRequest. Maybe that is not possible for compatibility reasons.

这篇关于使用HttpClient.SendAsync线程池,而不是异步IO?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆