(注意:我开始输入这个作为评论,但随着我在研究过程中获得了一些额外的知识,它变得足够大,可以回答)
1。 ThreadPool.QueueUserWorkItem
@ram 提供的前两个链接以及@Bryan Batchelders 的回答利用了有争议的 ThreadPool.QueueUserWorkItem。正如@Perhentian link 所示,这在 ASP.NET 环境中是有争议的,因为不小心使用可能使您的线程池饿死。
2。开始调用
然后我查看了@ram 的third link,它使用了BeginInvoke。从本质上讲,这似乎只是告诉一些代码也在另一个线程上运行。所以这里没有解决方案。
3。开始获取响应
现在回到@Perhentians link。它说明 BeginGetResponse 与实际线程有何不同,因为它使用 IO 完成端口 (IOPC)。因此,您实际上正在寻找的是一种仍然使用这些 IOPC 而不创建额外线程的解决方案。
现在,为了了解 .NET 是如何做到这一点的,我尝试深入研究 HttpWebRequest.BeginGetResponse,这实际上是一堆内部调用。它是这样的:
- HttpWebRequest.BeginGetResponse
- ServicePoint.SubmitRequest
- Connection.SubmitRequest
- 两个选项:
- 如果连接清晰:Connection.StartRequest
- 否则:Connection.WaitList.Add(request)
A.候补名单
首先让我们考虑选项 2:当与先前请求的连接完成时,将处理所述 WaitList。看一下整个链中涉及的 ConnectStream 会发现一个 ThreadPool.QueueUserWorkItem 带有备注:
// otherwise we queue a work item to parse the chunk
// Consider: Will we have an issue of thread dying off
// if we make a IO Read from that thread???
因此,至少在某些回退方案中,线程仍然可以通过使用 BeginGetResponse 无意中被框架生成!
B. Connection.StartRequest
现在,我们仍然有连接清晰的场景。回调由 System.Net.Sockets.Socket.BeginConnect 安装,并由 BeginAccept 实际调用。更多挖掘揭示了对 ThreadPool.UnsafeRegisterWaitForSingleObject 的调用,其结果用于等待。
结论
最后,我们可以知道在执行 BeginGetResponse 时实际发生了什么:
// 1. Connecting a socket
UnsafeNclNativeMethods.OSSOCK.WSAConnect(m_handle)
// 2. Registering the callback
m_RegisteredWait = ThreadPool.UnsafeRegisterWaitForSingleObject(m_AsyncEvent, s_RegisteredWaitCallback, this, Timeout.Infinite, true);
// 3. Waiting for the socket to complete
UnsafeNclNativeMethods.OSSOCK.WSAEventSelect(m_handle, m_AsyncEvent.SafeWaitHandle, blockEventBits);
注意 Timeout.Infinite。我仍在调查是否有可能让套接字运行不执行等待的代码路径,但目前看来,这对我来说是不可能的。
正如我的结论:在“即发即弃”场景中使用 IOCP 似乎没有简单的方法。因此,除非您可以让上述套接字不等待 BeginAccept 完成,否则您将被 ThreadPool 卡住。