在高流量场景中使用ASP.NET中的ThreadPool.QueueUserWorkItem

Mic*_*art 109 asp.net multithreading threadpool

我一直认为使用ThreadPool(比如说非关键的)短期背景任务被认为是最佳实践,即使在ASP.NET中也是如此,但后来我发现这篇文章似乎暗示了其他 -这个论点是你应该离开ThreadPool来处理ASP.NET相关的请求.

所以这就是我到目前为止一直在做的小异步任务:

ThreadPool.QueueUserWorkItem(s => PostLog(logEvent))
Run Code Online (Sandbox Code Playgroud)

而且文章建议明确地创建一个线程,类似于:

new Thread(() => PostLog(logEvent)){ IsBackground = true }.Start()
Run Code Online (Sandbox Code Playgroud)

第一种方法具有管理和限制的优点,但是有可能(如果文章是正确的)后台任务正在争夺具有ASP.NET请求处理程序的线程.第二种方法释放了ThreadPool,但代价是无限制,因此可能耗尽太多资源.

所以我的问题是,文章中的建议是否正确?

如果你的网站流量太大而你的ThreadPool已经满了,那么最好是带外,还是一个完整的ThreadPool意味着你无论如何都要达到你的资源限制,在这种情况下你不应该试图开始自己的线程?

澄清:我只是询问小型非关键异步任务(例如,远程日志记录)的范围,而不是需要单独过程的昂贵工作项(在这些情况下,我同意您需要更强大的解决方案).

Aar*_*ght 103

这里的其他答案似乎遗漏了最重要的一点:

除非您尝试并行化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和开头开始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中编写线程代码,请考虑是否可以使用预先存在的异步方法重写它,如果不能,那么请考虑您是否真的需要代码完全在后台线程中运行.在大多数情况下,您可能会增加复杂性而无需净收益.

  • I/O完成端口(IOCP).IOCP的描述不太正确.在IOCP中,您有一个静态的工作线程数,它们轮流处理所有挂起的任务.不要与可以修复或动态大小的线程池相混淆但是每个任务有一个线程 - 可怕地扩展.与ASYNC不同,每个任务没有一个线程.IOCP线程可以在任务1上稍微工作,然后切换到任务3,任务2,然后再次返回到任务1.任务会话状态被保存并在线程之间传递. (2认同)

Tim*_* P. 45

Microsoft的ASP.NET团队的Per Thomas Marquadt使用ASP.NET ThreadPool(QueueUserWorkItem)是安全的.

来自文章:

问)如果我的ASP.NET应用程序使用CLR ThreadPool线程,我不会饿死ASP.NET,它也使用CLR ThreadPool来执行请求吗? 大段引用

A)[T] o总结一下,不要担心线程会使ASP.NET匮乏,如果你认为这里存在问题,请告诉我,我们会处理它.

问)我应该创建自己的线程(新线程)吗?这对ASP.NET来说不会更好,因为它使用了CLR ThreadPool.

A)请不要.或者换一种方式,不!如果你真的很聪明 - 比我更聪明 - 那么你可以创建自己的线程; 否则,甚至不要考虑它.以下是您不应经常创建新线程的一些原因:

1)与QueueUserWorkItem相比,这是非常昂贵的...顺便说一句,如果你能写一个比CLR更好的ThreadPool,我鼓励你申请微软的工作,因为我们肯定在寻找像你这样的人!.