C# – 何时在高活动性服务器中使用标准线程,ThreadPool和TPL

我最近一直在阅读很多关于线程的内容,因为我正在寻求开发一个能够处理多达10,000-20,000个客户端的高性能,可扩展的TCP服务器,每个客户端都通过基于命令的方式与服务器进行双向通信。系统。 服务器将接收命令,并根据命令执行单个(或多个)任务。 我的问题是如何在各种情况下适当地使用.NET线程构造,执行可能需要一分钟到几个小时的任务,具体取决于正在执行的工作。

最令我困惑的是,无论我在哪里阅读,我都会看到“使用手动创建的线程(或自定义线程池)来处理’长期运行’任务,并使用TPL执行短期任务或任务需要并行处理。“ 究竟什么长期任务? 那是5秒,60秒,一小时吗?

我应该在什么时间框架中使用这三种创建线程的方法:

  • 手动创建的线程
  • .NET ThreadPool类
  • TPL

我想到的另一个问题如下 – 说我的服务器实际上连接了20,000个客户端,每个客户端每秒发送1个命令(可以转换为一个或多个任务)。 即使使用强大的硬件,我也不可能将过高的工作量推入到我拥有的任何线程池/工作项队列中,从而最终在队列缓慢填充到最大值后生成OutOfMemoryException?

任何见解将不胜感激。

实际上,对于那种情况, 所有这些都是次要的; 你要看的第一件事是asyc-IO,又名.BeginRead(...)等; 这允许您通过等待IO完成端口来最小化线程数 – 更高效。

一旦你有一个完整的消息, 在那个规模我会把消息扔进一个自定义的线程池/ synchronized-queue。 我将有一个受控数量的常规线程(不是池线程或IOCP)服务该队列来处理每个项目。

碰巧我正在做类似的事情(规模较小); 为了防止内存爆炸,我已经限制了工作队列; 如果它已满(即工作人员无法跟上),那么你可能会在一段时间内阻止IOCP,最终可能会超时,告诉客户端IOCP层“太忙”。

最令我困惑的是,无论我在哪里阅读,我都会看到“使用手动创建的线程(或自定义线程池)来处理’长期运行’任务,并使用TPL执行短期任务或任务需要并行处理。“

奇怪的建议,或者你可能误引了一点。 线程也能够并行处理,使用TPL,您可以使用LongRunning选项创建任务。 剩下的是你不应该在ThreadPool上启动长任务。

究竟什么是长期任务? 那是5秒,60秒,一小时吗?

TPL在ThreadPool之上运行,TP将以每秒2个的最大值创建新的线程。 所以长时间运行> = 500 ms


即使使用强大的硬件,我也没有机会将过高的工作量推入到我拥有的任何线程池/工作项队列中,

是的,没有线程工具可以扩展您的实际容量……

对于20k客户端,您可能需要一个服务器场,可以选择早期包含在您的设计中……

所以你应该在深入了解套接字之前给WCF一个好看的。

马克斯的建议就是我这样做的方式。 但是,如果任务花费的时间超过一秒,并且客户端每秒发送一个请求,则队列将稳定增加。

在这种情况下,我将使用一个服务器作为外观,它从客户端获取所有请求并以异步方式将响应发送回它们。

服务器会将所有请求放入一个由其他几个服务器读取的消息队列中。 这些服务器处理请求并将响应放在另一个由第一个服务器读取的消息队列中。

另一种解决方案是使用负载平衡服务器。

您似乎正在构建一个服务器,该服务器将为数千个并发请求提供服务,每个请求在几分钟到几小时内长时间运行。

通常,使线程工作负载足够短,最多可在几秒钟内完成。 任何更长的时间,您将开始占用服务器资源并严重影响服务器的可扩展性。 有数万个线程阻塞长时间运行的操作,或者同时执行那些长时间运行的操作,肯定会破坏您的可伸缩性。

不确定每次长时间运行消耗多少CPU时间。 这会影响您的设计,例如:

如果每个长时间运行主要在I / O上阻塞,则可以使用一个线程在重叠的I / O或I / O完成端口上等待,然后唤醒新线程以处理已完成的I / O(达到限制的限制) 。 您需要有一个限制数量的线程来为等待的连接提供服务。

如果每个长时间运行的操作都等待其他操作完成,请考虑Windows Workflow Foundation。

如果每个长时间运行的操作都消耗CPU,那么您不希望任何时候运行太多的操作,否则它会破坏您的服务器。 在这种情况下,使用MSMQ和/或TPL对任务进行排队,并确保只有少数同时运行。

在所有这些中,似乎您保持客户端连接打开。 最糟糕的事情是为每个连接保持一个线程阻塞。 您需要实现线程池策略,以便仅使用有限数量的线程来为所有未完成的连接提供服务。