这里的其他答案似乎忽略了最重要的一点:
除非您尝试并行化 CPU 密集型操作以便在低负载站点上更快地完成它,否则使用工作线程根本没有意义。
这适用于由创建的空闲线程和响应请求new Thread(...)
的工作线程。ThreadPool
QueueUserWorkItem
是的,这是真的,你可以ThreadPool
通过排队太多的工作项来饿死ASP.NET 进程。它将阻止 ASP.NET 处理进一步的请求。文章中的信息在这方面是准确的;用于的同一线程池QueueUserWorkItem
也用于服务请求。
但是,如果您实际上排队了足够多的工作项来导致这种饥饿,那么您应该使线程池饥饿!如果您同时运行数百个 CPU 密集型操作,那么在机器已经超载的情况下,让另一个工作线程为 ASP.NET 请求提供服务有什么好处?如果您遇到这种情况,您需要完全重新设计!
大多数时候,我看到或听说多线程代码在 ASP.NET 中被不恰当地使用,这并不是为了对 CPU 密集型工作进行排队。它用于排队 I/O 绑定的工作。如果你想做 I/O 工作,那么你应该使用 I/O 线程(I/O 完成端口)。
具体来说,您应该使用您正在使用的任何库类支持的异步回调。这些方法总是被非常清楚地标记;Begin
它们以and开头End
。如Stream.BeginRead
, Socket.BeginConnect
, WebRequest.BeginGetResponse
, 等等。
这些方法确实使用ThreadPool
,但它们使用 IOCP,它不会干扰 ASP.NET 请求。它们是一种特殊的轻量级线程,可以被来自 I/O 系统的中断信号“唤醒”。在 ASP.NET 应用程序中,通常每个工作线程都有一个 I/O 线程,因此每个请求都可以有一个异步操作排队。这实际上是数百个异步操作而没有任何显着的性能下降(假设 I/O 子系统可以跟上)。这比你需要的要多得多。
请记住,异步委托不是以这种方式工作的——它们最终会使用工作线程,就像ThreadPool.QueueUserWorkItem
. 只有 .NET Framework 库类的内置异步方法能够执行此操作。你可以自己做,但它很复杂而且有点危险,可能超出了本次讨论的范围。
在我看来,这个问题的最佳答案是不要在 ASP.NET 中使用ThreadPool
或背景Thread
实例。这根本不像在 Windows 窗体应用程序中启动线程,您这样做是为了保持 UI 响应并且不关心它的效率。在 ASP.NET 中,您关心的是吞吐量,并且所有这些工作线程上的所有上下文切换绝对会杀死您的吞吐量,无论您是否使用ThreadPool
。
请,如果您发现自己在 ASP.NET 中编写线程代码 - 考虑是否可以重写它以使用预先存在的异步方法,如果不能,那么请考虑您是否真的,真的需要代码完全在后台线程中运行。在大多数情况下,您可能会增加复杂性而没有净收益。