在ASP.NET在高流量情况下使用ThreadPool.QueueUserWorkItem(Usin

2019-06-18 23:00发布

我一直的印象是使用线程池的(假设非关键)短命的后台任务被认为是最好的做法,即使是在ASP.NET,但后来我碰上了这篇文章 ,这似乎并非如此-的论点是,你应该离开线程池来处理ASP.NET相关的请求。

因此,这里是如何我一直在做小的异步任务至今:

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

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

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

第一种方法中的管理和约束的优势,但有潜在的(如果文章是正确的)的后台任务,然后争夺与ASP.NET请求处理线程。 第二种方法释放线程池,但是,由于它是无界的,因此可能使用了太多的资源成本。

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

如果您的网站能获得这么多的流量,你的线程池快满了,然后是它更好地走出去,带外,或将一个完整的线程池意味着你得到你的资源的限制,无论如何,在这种情况下你不应该尝试开始自己的线程?

澄清:我只是要求在小非关键异步任务(例如,远程登录),而不是需要一个单独的进程(在这些情况下,我同意你需要一个更强大的解决方案),昂贵的工作项目的范围。

Answer 1:

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

除非你试图以一个并行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完成端口)。

具体来说,您应该使用什么库班你使用支持异步回调。 这些方法总是很清楚的标记; 他们开始的话BeginEnd 。 作为Stream.BeginReadSocket.BeginConnectWebRequest.BeginGetResponse ,等等。

这些方法使用ThreadPool ,但他们使用IOCPS, 与ASP.NET请求干扰。 他们是一个特殊的轻量级线程从I / O系统的中断信号可以被“唤醒”。 而在ASP.NET应用程序中,你通常有一个I /每个工作线程O线程,因此每一个请求可以有一个异步操作排队。 这是数以百计的异步操作没有任何显著的性能下降(假设I / O子系统能够跟上)。 它的方式超过你永远需要。

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

最好的回答了这个问题,在我看来,是不使用ThreadPool 后台Thread在ASP.NET实例 。 这一点不像在Windows纺一个线程窗体应用程序,你这样做是为了保持UI响应,并不在乎它是多么有效。 在ASP.NET中,你关注的是吞吐量 ,以及所有这方面的所有工作线程切换是绝对会是否使用你的吞吐量ThreadPool与否。

请,如果你发现自己写的线程代码在ASP.NET - 考虑是否可以改写使用预先存在的异步方法,如果不能,那么请考虑您是否真的,真的需要的代码在后台线程运行在所有。 在大多数情况下,你可能会因为没有净效益增加复杂性。



Answer 2:

在微软的ASP.NET团队的每托马斯Marquadt,它是安全的使用ASP.NET线程池(QueueUserWorkItem)。

从文章 :

Q)如果我的ASP.NET应用程序使用CLR线程池线程,不会饿死我ASP.NET,后者也使用CLR线程池执行请求? 大段引用

A)[T] O总结,不用担心挨饿线程的ASP.NET,如果你觉得这里有一个问题让我知道,我们会照顾它。

Q)我应该创建自己的线程(新的Thread)? 这会不会是更好地为ASP.NET,因为它使用CLR线程池。

A)请不要。 或者换一种不同的方式,没有! 如果你真的很聪明,要比我聪明,那么你就可以创建自己的线程; 否则,就别想了。 这里有一些原因,你应该不会频繁地创建新主题:

1)这是非常昂贵的,比起QueueUserWorkItem ...顺便说一下,如果你可以写比CLR的更好线程池,我鼓励你申请在微软的工作,因为我们肯定找你这样的人!



Answer 3:

网站不应该到处去产卵线程。

您通常移到这个功能伸到一个Windows服务,然后你会得到(我用的MSMQ与他们交谈)进行通信。

- 编辑

:我在这里描述的实施方式在ASP.NET MVC Web应用程序基于队列的后台处理

- 编辑

为了扩大为什么这不仅仅是线程甚至更好:

使用MSMQ,你可以传达到另一台服务器。 你可以写在计算机之间一个队列,所以如果你决定,出于某种原因,你的后台任务是利用了主服务器的资源太多,你可以移动它很平凡。

它也可以让你的任何任务,你试图做批量处理(发送邮件/其他)。



Answer 4:

我绝对认为,在ASP.NET快速,低优先级的异步工作一般的做法是,只要你想你的资源是有界使用.NET线程池,特别是对于高流量的情况。

另外,线程的实现是隐藏的 - 如果你开始产卵你自己的线程,你必须正确地管理它们。 不是说你不能这样做,但为什么要重复那个轮子?

如果性能成为一个问题,你可以建立线程池的限制因素(而不是数据库连接,传出的网络连接,内存页超时等),然后你调整线程池配置,以允许更多的工作线程,更高排队的请求等等。

如果您没有性能问题,然后选择以产生新的线程,以减少争用ASP.NET请求队列是经典过早的优化。

理想情况下,你不会需要使用一个单独的线程做虽然登记操作 - 只允许原来的线程尽可能快地完成操作,这就是MSMQ和一个独立的消费者线程/进程进来的图片。 我同意这是贯彻落实重,更多的工作,但你真的需要在这里耐久性 - 共享的内存队列将很快磨损其欢迎的波动。



Answer 5:

您应该使用QueueUserWorkItem,并避免产生新的线程就像你会避免瘟疫。 对于视觉这可以解释为什么你会不会饿死ASP.NET,因为它使用相同的线程池,想象用双手保持半打保龄球瓶,剑,或任何在飞行非常熟练魔术师。 对于视觉的,为什么创建自己的线程是坏的,可以想象上下班高峰期在西雅图发生的事情时频繁使用的入口坡道高速公路允许车辆进入交通立即而不是使用一盏灯,限制进入的数目,以一个每隔几秒钟。 最后,进行了详细的说明,请访问以下链接:

http://blogs.msdn.com/tmarq/archive/2010/04/14/performing-asynchronous-work-or-tasks-in-asp-net-applications.aspx

谢谢,托马斯



Answer 6:

这篇文章是不正确的。 ASP.NET有它的线程自己的游泳池,管理工作线程,为服务ASP.NET请求。 该池通常是几百个线程,从线程池池,这是处理器的一些规模较小的多个独立。

在ASP.NET中使用线程池不会与ASP.NET工作线程干扰。 使用线程池的罚款。

这也将是可接受的设置的单个线程这仅仅是用于日志消息使用生产者/消费者图案到日志消息传递给该线程。 在这种情况下,由于线程是长期存在的,你应该创建一个新的线程来运行记录。

使用一个新的线程为每个消息肯定是矫枉过正。

另一种选择,如果你只是在谈论记录,就是用一个库像log4net的。 它可以处理在一个单独的线程日志,并采取一切可能拿出在场景的背景问题的照顾。



Answer 7:

我想说的文章是错误的。 如果你正在运行一个大的.NET店,你可以放心地使用跨多个应用程序和多个网站池(使用单独的应用程序池),仅仅基于在一个声明中线程池的文档:

有每个进程一个线程池。 线程池具有的每个可用处理器250个工作线程,和1000 I / O完成线程的默认大小。 线程池中的线程的数目可以通过使用SetMaxThreads方法来改变。 每个线程使用默认的堆栈大小,并在默认的优先级运行。



Answer 8:

我上周在问工作过类似的问题,我给你相同的答案。 你为什么每次请求的多线程Web应用程序? Web服务器是高度优化的,提供及时的许多要求一个梦幻般的系统(即多线程)。 想想会发生什么,当你请求网络上的几乎任何页面。

  1. 请求是对于一些网页制作
  2. HTML是担任回来
  3. 在HTML告诉客户做出进一步requets(JS,CSS,图像等..)
  4. 更多信息送达回

你给远程登录的例子,但应该是你的记录器的一个问题。 一个异步过程要到位及时接收消息。 萨姆甚至指出,你的记录器(log4net的)应该已经支持这一点。

山姆也是正确的,使用的CLR线程池,将不会导致IIS中的线程池的问题。 事情到这里与来虽然担心,就是你不产卵从处理线程,你的线程池线程产卵的新主题关闭IIS的。 是有区别和区别是非常重要的。

线程VS过程

两个线程和进程是并行的应用程序的方法。 然而,过程是包含自己的状态信息,使用自己的地址空间,并通过进程间通信机制(通常由操作系统进行管理)只与彼此交互独立的执行单元。 应用在设计阶段通常被划分成的过程,以及主处理明确地派生子过程时是有意义的在逻辑上分开显著应用程序的功能。 过程,换言之,是一个结构构造。

相比之下,一个线程是一个编码结构,不影响应用程序的体系结构。 单个进程可以包含多个线程; 一个进程内的所有线程共享相同的状态和相同的内存空间,并且可以彼此直接通信,因为它们共享相同的变量。

资源



Answer 9:

我不同意引用的文章(C#feeds.com)同意。 这是很容易创建一个新的线程,但危险的。 活动线程的最佳数量在单一内核上运行实际上是出奇的低 - 低于10这是太容易造成机器如果轻微的任务创建的线程浪费时间切换线程。 线程是需要管理的资源。 工作项目的抽象是有处理这个问题。

有在这里下车减少了可用于请求的线程数量和创建过多的线程,让其中的任何有效处理之间的贸易。 这是一个非常有活力的情况,但我认为一个人应该(通过线程池在这种情况下)进行主动管理,而不是把它留给之处理,以保持领先的创建线程的。

最后,文章对有关使用线程池的危险,一些非常笼统的声明,但它确实需要一些具体的备份。



Answer 10:

无论IIS使用相同的线程池来处理传入的请求似乎很难得到一个明确的答案,也似乎已经改变了的版本。 因此,它似乎是一个好主意,不要过度使用线程池线程,因此IIS具有可他们中的很多。 在另一方面,产卵自己的线程为每个小任务似乎是一个坏主意。 想必,你有某种在你的记录锁定的,所以只有一个线程可以一次进步,其余的将只是轮流得到定期和不定期的(更不用说产生一个新的线程的开销)。 从本质上讲,你碰上的线程池的目的是要避免的确切问题。

看来,一个合理的妥协将是您的应用程序分配一个记录线程,你可以传递消息。 你会要小心,在发送的消息是尽可能快地让你不要你的应用程序变慢。



Answer 11:

您可以使用的Parallel.For或Parallel.ForEach和定义要分配给平稳运行,防止池饥饿可能线程的限制。

然而,被在后台运行,你需要在ASP.Net Web应用程序使用在纯TPL风格。

var ts = new CancellationTokenSource();
CancellationToken ct = ts.Token;

ParallelOptions po = new ParallelOptions();
            po.CancellationToken = ts.Token;
            po.MaxDegreeOfParallelism = 6; //limit here

 Task.Factory.StartNew(()=>
                {                        
                  Parallel.ForEach(collectionList, po, (collectionItem) =>
                  {
                     //Code Here PostLog(logEvent);
                  }
                });


文章来源: Using ThreadPool.QueueUserWorkItem in ASP.NET in a high traffic scenario