我读到this article并了解一些有关 IHttpAsyncHandler 的信息。
根据下图,当我调用 AsyncHandler 时,创建了 2 个线程。
当有请求到来时,IIS会抢一个线程——线程1来处理这个请求,当它调用beginXXX方法时,就会创建线程2来处理这个真正的逻辑。
我的问题是:
当线程2运行时,连接仍然保持,等待响应。
当线程2运行时,线程1的状态是什么?是在 sleep 吗?或者已经发布了?
如果线程1正在 sleep ,当线程2完成时,线程1被唤醒并发送响应?
如果线程1被释放,当线程2结束时,是否有一个新的线程被创建为线程1?这个新线程 1 将响应发送给客户端。
线程1和线程2在同一个线程池中,不是吗? 如果是这样,可用线程数是平衡的,这样做的目的是什么?
最佳答案
您并没有将线程池视为一个池。当“线程 1”在 PreRender 完成其工作时,它将返回到池中以供任何目的重用。当数据准备好时,End
将从线程池中选择一个随机线程并完成其工作并发送响应。该随机线程可能是一个新线程,也可能是之前完成工作但返回到池中的同一线程。
重点是在 Begin
和 End
之间的窗口中,线程已重新调整到池中,并且可以为其他连接提供服务。这允许您拥有比系统可以处理的并发线程更多的并发连接,在同步版本中,一旦达到并发线程的最大数量,您就无法再处理新请求。
让我详细介绍一下正在发生的事情
这里是两个同步连接请求的时间线,以显示为基线,为了简单起见,我们会说每个步骤都需要 1 毫秒来执行,除了 PreRender
需要 19 毫秒才能完成。
╔═══════╦════════════════════════╦══════════════════╦═════════════════╦═══════════════════╗ ║ Time ║ Action (Connection #) ║ Open Connections ║ Running Threads ║ Available Threads ║ ╠═══════╬════════════════════════╬══════════════════╬═════════════════╬═══════════════════╣ ║ 0 ms ║ Connection Request (1) ║ 0 ║ 0 ║ 2 ║ ║ 1 ms ║ PreInit (1) ║ 1 ║ 1 ║ 1 ║ ║ 2 ms ║ Init (1) ║ 1 ║ 1 ║ 1 ║ ║ 3 ms ║ InitComplete (1) ║ 1 ║ 1 ║ 1 ║ ║ 4 ms ║ PreLoad (1) ║ 1 ║ 1 ║ 1 ║ ║ 5 ms ║ LoadComplete (1) ║ 1 ║ 1 ║ 1 ║ ║ 6 ms ║ PreRender (1) ║ 1 ║ 1 ║ 1 ║ ║ 10 ms ║ Connection Request (2) ║ 1 ║ 1 ║ 1 ║ ║ 11 ms ║ PreInit (2) ║ 2 ║ 2 ║ 0 ║ ║ 12 ms ║ Init (2) ║ 2 ║ 2 ║ 0 ║ ║ 13 ms ║ InitComplete (2) ║ 2 ║ 2 ║ 0 ║ ║ 14 ms ║ PreLoad (2) ║ 2 ║ 2 ║ 0 ║ ║ 15 ms ║ LoadComplete (2) ║ 2 ║ 2 ║ 0 ║ ║ 16 ms ║ PreRender (2) ║ 2 ║ 2 ║ 0 ║ ║ 25 ms ║ PreRenderComplete (1) ║ 2 ║ 2 ║ 0 ║ ║ 26 ms ║ SaveState (1) ║ 2 ║ 2 ║ 0 ║ ║ 27 ms ║ SaveStateComplete (1) ║ 2 ║ 2 ║ 0 ║ ║ 28 ms ║ Render (1) ║ 2 ║ 2 ║ 0 ║ ║ 29 ms ║ Send Response (1) ║ 1 ║ 1 ║ 1 ║ ║ 35 ms ║ PreRenderComplete (2) ║ 1 ║ 1 ║ 1 ║ ║ 36 ms ║ SaveState (2) ║ 1 ║ 1 ║ 1 ║ ║ 37 ms ║ SaveStateComplete (2) ║ 1 ║ 1 ║ 1 ║ ║ 38 ms ║ Render (2) ║ 1 ║ 1 ║ 1 ║ ║ 39 ms ║ Send Response (2) ║ 0 ║ 0 ║ 2 ║ ╚═══════╩════════════════════════╩══════════════════╩═════════════════╩═══════════════════╝
这是异步版本的时间表。
╔═══════╦════════════════════════╦══════════════════╦═════════════════╦═══════════════════╗ ║ Time ║ Action (Connection #) ║ Open Connections ║ Running Threads ║ Available Threads ║ ╠═══════╬════════════════════════╬══════════════════╬═════════════════╬═══════════════════╣ ║ 0 ms ║ Connection Request (1) ║ 0 ║ 0 ║ 2 ║ ║ 1 ms ║ PreInit (1) ║ 1 ║ 1 ║ 1 ║ ║ 2 ms ║ Init (1) ║ 1 ║ 1 ║ 1 ║ ║ 3 ms ║ InitComplete (1) ║ 1 ║ 1 ║ 1 ║ ║ 4 ms ║ PreLoad (1) ║ 1 ║ 1 ║ 1 ║ ║ 5 ms ║ LoadComplete (1) ║ 1 ║ 1 ║ 1 ║ ║ 6 ms ║ PreRender (1) ║ 1 ║ 1 ║ 1 ║ ║ 7 ms ║ Begin (1) ║ 1 ║ 0 ║ 2 ║ ║ 10 ms ║ Connection Request (2) ║ 1 ║ 0 ║ 2 ║ ║ 11 ms ║ PreInit (2) ║ 2 ║ 1 ║ 1 ║ ║ 12 ms ║ Init (2) ║ 2 ║ 1 ║ 1 ║ ║ 13 ms ║ InitComplete (2) ║ 2 ║ 1 ║ 1 ║ ║ 14 ms ║ PreLoad (2) ║ 2 ║ 1 ║ 1 ║ ║ 15 ms ║ LoadComplete (2) ║ 2 ║ 1 ║ 1 ║ ║ 16 ms ║ PreRender (2) ║ 2 ║ 1 ║ 1 ║ ║ 17 ms ║ Begin (2) ║ 2 ║ 0 ║ 2 ║ ║ 25 ms ║ End (1) ║ 2 ║ 1 ║ 1 ║ ║ 26 ms ║ PreRenderComplete (1) ║ 2 ║ 1 ║ 1 ║ ║ 27 ms ║ SaveState (1) ║ 2 ║ 1 ║ 1 ║ ║ 28 ms ║ SaveStateComplete (1) ║ 2 ║ 1 ║ 1 ║ ║ 29 ms ║ Render (1) ║ 2 ║ 1 ║ 1 ║ ║ 30 ms ║ Send Response (1) ║ 1 ║ 0 ║ 2 ║ ║ 35 ms ║ End (2) ║ 1 ║ 1 ║ 1 ║ ║ 36 ms ║ PreRenderComplete (2) ║ 1 ║ 1 ║ 1 ║ ║ 37 ms ║ SaveState (2) ║ 1 ║ 1 ║ 1 ║ ║ 38 ms ║ SaveStateComplete (2) ║ 1 ║ 1 ║ 1 ║ ║ 39 ms ║ Render (2) ║ 1 ║ 1 ║ 1 ║ ║ 40 ms ║ Send Response (2) ║ 0 ║ 0 ║ 2 ║ ╚═══════╩════════════════════════╩══════════════════╩═════════════════╩═══════════════════╝
现在,当处理两个或更少的连接时,异步并没有真正的好处,事实上,由于额外的开销,它甚至可能会稍微慢一些。
但是看看当我们有超过 2 个并发连接时会发生什么。
同步:
╔═══════╦════════════════════════╦══════════════════╦═════════════════╦═══════════════════╗ ║ Time ║ Action (Connection #) ║ Open Connections ║ Running Threads ║ Available Threads ║ ╠═══════╬════════════════════════╬══════════════════╬═════════════════╬═══════════════════╣ ║ 0 ms ║ Connection Request (1) ║ 0 ║ 0 ║ 2 ║ ║ 1 ms ║ PreInit (1) ║ 1 ║ 1 ║ 1 ║ ║ 2 ms ║ Init (1) ║ 1 ║ 1 ║ 1 ║ ║ 3 ms ║ InitComplete (1) ║ 1 ║ 1 ║ 1 ║ ║ 4 ms ║ PreLoad (1) ║ 1 ║ 1 ║ 1 ║ ║ 5 ms ║ LoadComplete (1) ║ 1 ║ 1 ║ 1 ║ ║ 6 ms ║ PreRender (1) ║ 1 ║ 1 ║ 1 ║ ║ 10 ms ║ Connection Request (2) ║ 1 ║ 1 ║ 1 ║ ║ 11 ms ║ PreInit (2) ║ 2 ║ 2 ║ 0 ║ ║ 12 ms ║ Init (2) ║ 2 ║ 2 ║ 0 ║ ║ 13 ms ║ InitComplete (2) ║ 2 ║ 2 ║ 0 ║ ║ 14 ms ║ PreLoad (2) ║ 2 ║ 2 ║ 0 ║ ║ 15 ms ║ LoadComplete (2) ║ 2 ║ 2 ║ 0 ║ ║ 16 ms ║ PreRender (2) ║ 2 ║ 2 ║ 0 ║ ║ 20 ms ║ Connection Request (3) ║ 2 ║ 2 ║ 0 ║ ║ 25 ms ║ PreRenderComplete (1) ║ 2 ║ 2 ║ 0 ║ ║ 26 ms ║ SaveState (1) ║ 2 ║ 2 ║ 0 ║ ║ 27 ms ║ SaveStateComplete (1) ║ 2 ║ 2 ║ 0 ║ ║ 28 ms ║ Render (1) ║ 2 ║ 2 ║ 0 ║ ║ 29 ms ║ Send Response (1) ║ 1 ║ 1 ║ 1 ║ ║ 30 ms ║ PreInit (3) ║ 2 ║ 2 ║ 0 ║ ║ 31 ms ║ Init (3) ║ 2 ║ 2 ║ 0 ║ ║ 32 ms ║ InitComplete (3) ║ 2 ║ 2 ║ 0 ║ ║ 33 ms ║ PreLoad (3) ║ 2 ║ 2 ║ 0 ║ ║ 34 ms ║ LoadComplete (3) ║ 2 ║ 2 ║ 0 ║ ║ 35 ms ║ PreRender (3) ║ 2 ║ 2 ║ 0 ║ ║ 35 ms ║ PreRenderComplete (2) ║ 2 ║ 2 ║ 0 ║ ║ 36 ms ║ SaveState (2) ║ 2 ║ 2 ║ 0 ║ ║ 37 ms ║ SaveStateComplete (2) ║ 2 ║ 2 ║ 0 ║ ║ 38 ms ║ Render (2) ║ 2 ║ 2 ║ 0 ║ ║ 39 ms ║ Send Response (2) ║ 1 ║ 1 ║ 1 ║ ║ 54 ms ║ PreRenderComplete (3) ║ 1 ║ 1 ║ 1 ║ ║ 55 ms ║ SaveState (3) ║ 1 ║ 1 ║ 1 ║ ║ 56 ms ║ SaveStateComplete (3) ║ 1 ║ 1 ║ 1 ║ ║ 57 ms ║ Render (3) ║ 1 ║ 1 ║ 1 ║ ║ 58 ms ║ Send Response (3) ║ 0 ║ 0 ║ 2 ║ ╚═══════╩════════════════════════╩══════════════════╩═════════════════╩═══════════════════╝
异步:
╔═══════╦════════════════════════╦══════════════════╦═════════════════╦═══════════════════╗ ║ Time ║ Action (Connection #) ║ Open Connections ║ Running Threads ║ Available Threads ║ ╠═══════╬════════════════════════╬══════════════════╬═════════════════╬═══════════════════╣ ║ 0 ms ║ Connection Request (1) ║ 0 ║ 0 ║ 2 ║ ║ 1 ms ║ PreInit (1) ║ 1 ║ 1 ║ 1 ║ ║ 2 ms ║ Init (1) ║ 1 ║ 1 ║ 1 ║ ║ 3 ms ║ InitComplete (1) ║ 1 ║ 1 ║ 1 ║ ║ 4 ms ║ PreLoad (1) ║ 1 ║ 1 ║ 1 ║ ║ 5 ms ║ LoadComplete (1) ║ 1 ║ 1 ║ 1 ║ ║ 6 ms ║ PreRender (1) ║ 1 ║ 1 ║ 1 ║ ║ 7 ms ║ Begin (1) ║ 1 ║ 0 ║ 2 ║ ║ 10 ms ║ Connection Request (2) ║ 1 ║ 0 ║ 2 ║ ║ 11 ms ║ PreInit (2) ║ 2 ║ 1 ║ 1 ║ ║ 12 ms ║ Init (2) ║ 2 ║ 1 ║ 1 ║ ║ 13 ms ║ InitComplete (2) ║ 2 ║ 1 ║ 1 ║ ║ 14 ms ║ PreLoad (2) ║ 2 ║ 1 ║ 1 ║ ║ 15 ms ║ LoadComplete (2) ║ 2 ║ 1 ║ 1 ║ ║ 16 ms ║ PreRender (2) ║ 2 ║ 1 ║ 1 ║ ║ 17 ms ║ Begin (2) ║ 2 ║ 0 ║ 2 ║ ║ 20 ms ║ Connection Request (3) ║ 3 ║ 0 ║ 2 ║ ║ 21 ms ║ PreInit (3) ║ 3 ║ 1 ║ 1 ║ ║ 22 ms ║ Init (3) ║ 3 ║ 1 ║ 1 ║ ║ 23 ms ║ InitComplete (3) ║ 3 ║ 1 ║ 1 ║ ║ 24 ms ║ PreLoad (3) ║ 3 ║ 1 ║ 1 ║ ║ 25 ms ║ End (1) ║ 3 ║ 2 ║ 0 ║ ║ 25 ms ║ LoadComplete (3) ║ 3 ║ 2 ║ 0 ║ ║ 26 ms ║ PreRenderComplete (1) ║ 3 ║ 2 ║ 0 ║ ║ 26 ms ║ PreRender (3) ║ 3 ║ 2 ║ 0 ║ ║ 27 ms ║ SaveState (1) ║ 3 ║ 2 ║ 0 ║ ║ 27 ms ║ Begin (3) ║ 3 ║ 1 ║ 0 ║ ║ 28 ms ║ SaveStateComplete (1) ║ 3 ║ 1 ║ 1 ║ ║ 29 ms ║ Render (1) ║ 3 ║ 1 ║ 1 ║ ║ 30 ms ║ Send Response (1) ║ 2 ║ 0 ║ 2 ║ ║ 35 ms ║ End (2) ║ 2 ║ 1 ║ 1 ║ ║ 36 ms ║ PreRenderComplete (2) ║ 2 ║ 1 ║ 1 ║ ║ 37 ms ║ SaveState (2) ║ 2 ║ 1 ║ 1 ║ ║ 38 ms ║ SaveStateComplete (2) ║ 2 ║ 1 ║ 1 ║ ║ 39 ms ║ Render (2) ║ 2 ║ 1 ║ 1 ║ ║ 40 ms ║ Send Response (2) ║ 1 ║ 0 ║ 2 ║ ║ 45 ms ║ End (3) ║ 1 ║ 1 ║ 1 ║ ║ 46 ms ║ PreRenderComplete (3) ║ 1 ║ 1 ║ 1 ║ ║ 47 ms ║ SaveState (3) ║ 1 ║ 1 ║ 1 ║ ║ 48 ms ║ SaveStateComplete (3) ║ 1 ║ 1 ║ 1 ║ ║ 49 ms ║ Render (3) ║ 1 ║ 1 ║ 1 ║ ║ 50 ms ║ Send Response (3) ║ 0 ║ 0 ║ 2 ║ ╚═══════╩════════════════════════╩══════════════════╩═════════════════╩═══════════════════╝
请注意,在同步版本中,连接 3 在 20 毫秒后进入,但必须等到 30 毫秒后才有线程可用于处理请求。相比之下,异步版本在等待 PreRender 完成时将线程放回到池中,因此连接 3 立即能够开始处理。
因此,使用异步不会增加每秒事务数,事实上它可能会稍微降低它。然而,它所做的是增加您的“最大并发事务”计数并增加您的总吞吐量。
关于c# - IHttpAsyncHandler 的线程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25141344/