本文介绍了高流量场景下在ASP.NET中使用ThreadPool.QueueUserWorkItem的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直认为,即使在 ASP.NET 中,将 ThreadPool 用于(假设是非关键的)短期后台任务也被认为是最佳实践,但后来我遇到了 这篇文章 似乎另有建议 - 论点是您应该离开 ThreadPool 来处理 ASP.NET相关请求.

I've always been under the impression that using the ThreadPool for (let's say non-critical) short-lived background tasks was considered best practice, even in ASP.NET, but then I came across this article that seems to suggest otherwise - the argument being that you should leave the ThreadPool to deal with ASP.NET related requests.

到目前为止,我是这样处理小型异步任务的:

So here's how I've been doing small asynchronous tasks so far:

ThreadPool.QueueUserWorkItem(s => PostLog(logEvent))

并且文章建议改为显式创建线程,类似于:

And the article is suggesting instead to create a thread explicitly, similar to:

new Thread(() => PostLog(logEvent)){ IsBackground = true }.Start()

第一种方法的优点是可管理和有界,但有可能(如果文章是正确的)后台任务会与 ASP.NET 请求处理程序争夺线程.第二种方法释放了 ThreadPool,但代价是不受限制,因此可能会占用过多资源.

The first method has the advantage of being managed and bounded, but there's the potential (if the article is correct) that the background tasks are then vying for threads with ASP.NET request-handlers. The second method frees up the ThreadPool, but at the cost of being unbounded and thus potentially using up too many resources.

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

So my question is, is the advice in the article correct?

如果您的网站获得的流量如此之大以至于您的 ThreadPool 已满,那么最好带外,还是一个完整的 ThreadPool 意味着您无论如何都已达到资源的极限,在哪种情况下您不应该尝试启动自己的线程?

If your site was getting so much traffic that your ThreadPool was getting full, then is it better to go out-of-band, or would a full ThreadPool imply that you're getting to the limit of your resources anyway, in which case you shouldn't be trying to start your own threads?

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

Clarification: I'm just asking in the scope of small non-critical asynchronous tasks (eg, remote logging), not expensive work items that would require a separate process (in these cases I agree you'll need a more robust solution).

推荐答案

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

Other answers here seem to be leaving out the most important point:

除非您尝试并行化 CPU 密集型操作以在低负载站点上更快地完成它,否则使用工作线程根本没有意义.

这适用于由 new Thread(...) 创建的空闲线程和 ThreadPool 中响应 QueueUserWorkItem 的工作线程> 请求.

That goes for both free threads, created by new Thread(...), and worker threads in the ThreadPool that respond to QueueUserWorkItem requests.

是的,这是真的,您可以通过将太多工作项排队而使 ASP.NET 进程中的 ThreadPool 饿死.它将阻止 ASP.NET 处理进一步的请求.文章中的信息在这方面是准确的;用于 QueueUserWorkItem 的线程池也用于处理请求.

Yes, it's true, you can starve the ThreadPool in an ASP.NET process by queuing too many work items. It will prevent ASP.NET from processing further requests. The information in the article is accurate in that respect; the same thread pool used for QueueUserWorkItem is also used to serve requests.

但是,如果您实际上排队的工作项数量足以导致这种饥饿,那么您应该就会使线程池处于饥饿状态!如果您同时运行数以百计的 CPU 密集型操作,当机器已经过载时,让另一个工作线程为 ASP.NET 请求提供服务有什么好处?如果您遇到这种情况,则需要彻底重新设计!

But if you are actually queuing enough work items to cause this starvation, then you should be starving the thread pool! If you are running literally hundreds of CPU-intensive operations at the same time, what good would it do to have another worker thread to serve an ASP.NET request, when the machine is already overloaded? If you're running into this situation, you need to redesign completely!

大多数时候我看到或听到多线程代码在 ASP.NET 中被不当使用,它不是用于排队 CPU 密集型工作.它用于排队 I/O 绑定的工作.而且如果你想做 I/O 工作,那么你应该使用一个 I/O 线程(I/O 完成端口).

Most of the time I see or hear about multi-threaded code being inappropriately used in ASP.NET, it's not for queuing CPU-intensive work. It's for queuing I/O-bound work. And if you want to do I/O work, then you should be using an I/O thread (I/O Completion Port).

具体来说,您应该使用您正在使用的任何库类支持的异步回调.这些方法总是有非常明确的标签;它们以 BeginEnd 开头.如 Stream.BeginReadSocket.BeginConnectWebRequest.BeginGetResponse 等.

Specifically, you should be using the async callbacks supported by whatever library class you're using. These methods are always very clearly labeled; they start with the words Begin and End. As in Stream.BeginRead, Socket.BeginConnect, WebRequest.BeginGetResponse, and so on.

这些方法确实使用ThreadPool,但是它们使用IOCP,它们不会干扰ASP.NET 请求.它们是一种特殊的轻量级线程,可以被来自 I/O 系统的中断信号唤醒".在 ASP.NET 应用程序中,通常每个工作线程都有一个 I/O 线程,因此每个请求都可以有一个异步操作排队.这实际上是数百个异步操作,而没有任何显着的性能下降(假设 I/O 子系统可以跟上).它远远超出您的需要.

These methods do use the ThreadPool, but they use IOCPs, which do not interfere with ASP.NET requests. They are a special kind of lightweight thread that can be "woken up" by an interrupt signal from the I/O system. And in an ASP.NET application, you normally have one I/O thread for each worker thread, so every single request can have one async operation queued up. That's literally hundreds of async operations without any significant performance degradation (assuming the I/O subsystem can keep up). It's way more than you'll ever need.

请记住异步委托不会以这种方式工作 - 他们最终会使用工作线程,就像 ThreadPool.QueueUserWorkItem 一样.只有 .NET Framework 库类的内置异步方法能够执行此操作.你可以自己做,但它很复杂,有点危险,可能超出了本讨论的范围.

Just keep in mind that async delegates do not work this way - they'll end up using a worker thread, just like ThreadPool.QueueUserWorkItem. It's only the built-in async methods of the .NET Framework library classes that are capable of doing this. You can do it yourself, but it's complicated and a little bit dangerous and probably beyond the scope of this discussion.

在我看来,这个问题的最佳答案是不要使用 ThreadPool 后台 Thread 实例在 ASP.NET 中.这完全不像在 Windows 窗体应用程序中启动一个线程,你这样做是为了保持 UI 响应,而不关心它的效率如何.在 ASP.NET 中,您关心的是吞吐量,无论您使用 ThreadPool 与否.

The best answer to this question, in my opinion, is don't use the ThreadPool or a background Thread instance in ASP.NET. It's not at all like spinning up a thread in a Windows Forms application, where you do it to keep the UI responsive and don't care about how efficient it is. In ASP.NET, your concern is throughput, and all that context switching on all those worker threads is absolutely going to kill your throughput whether you use the ThreadPool or not.

请,如果您发现自己在 ASP.NET 中编写线程代码 - 考虑是否可以重写它以使用预先存在的异步方法,如果不能,那么请考虑您是否真的,真的完全需要代码在后台线程中运行.在大多数情况下,您可能会增加复杂性而没有任何净收益.

Please, if you find yourself writing threading code in ASP.NET - consider whether or not it could be rewritten to use pre-existing asynchronous methods, and if it can't, then please consider whether or not you really, truly need the code to run in a background thread at all. In the majority of cases, you will probably be adding complexity for no net benefit.

这篇关于高流量场景下在ASP.NET中使用ThreadPool.QueueUserWorkItem的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-19 22:07