【问题标题】:Servers and threading models服务器和线程模型
【发布时间】:2010-08-22 20:10:51
【问题描述】:

我对以下概念感到困扰: 大多数书籍/文档都描述了多线程服务器的健壮性,最常见的方法是启动一个新线程来为每个新客户端提供服务。例如。一个线程专用于每个新连接。但这实际上是如何在大型系统中实现的呢?如果我们有一个服务器接受来自 100000 个客户端的请求,它已经启动了 100000 个线程?这是现实的吗?一个服务器可以运行多少个线程没有限制吗?另外上下文切换和同步的开销,它不会降低性能吗?它是作为队列和线程的混合实现的吗?在这种情况下,队列的数量是固定的吗?任何人都可以启发我,也许给我一个很好的参考来描述这些?

谢谢!

【问题讨论】:

  • 一个 .NET 线程的堆栈大小为 1MB,100000 个线程需要 100GB 的 RAM 才能保持线程运行,显然不现实。

标签: java c++ multithreading performance


【解决方案1】:

常用的方法是使用线程池。线程池是已创建线程的集合。当一个新请求到达服务器时,它会从池中分配一个备用线程。处理完请求后,线程返回池中。

池中的线程数根据应用程序的特性进行配置。例如,如果您有一个受 CPU 限制的应用程序,您将不需要太多线程,因为上下文切换会降低性能。另一方面,如果您有一个 DB 或 IO 绑定的应用程序,您需要更多的线程,因为需要花费大量时间等待。因此,更多的线程将更好地利用 CPU。

谷歌“线程池”,你肯定会发现很多关于这个概念的内容。

【讨论】:

  • @Kristoffer:看看线程池,我的理解是池中的线程数应该始终与可用处理器的数量相关。如果是这样,那么池中的线程数相对于请求数总是很低。如果我的理解是正确的,那么在我的示例中,假设有 48 个核心处理器的机器,应该只汇集 48 个线程,其余请求应该排队?我混合了这些概念吗?
  • 在决定需要的线程数量时,您应该始终考虑可用的处理器数量。但是,您还需要考虑线程所做的工作类型。让我们举两个例子。 1)您的线程只执行需要 CPU 的计算,没有 IO 或其他等待。然后,拥有与 CPU 相同数量的线程是有意义的,因为这样可以避免在空闲时浪费 CPU 周期,同时最大限度地减少上下文切换。 2) 您的线程花费 50% 的时间等待外部系统。那么有 2x 个线程来充分利用 CPU 是有意义的。
  • @Kristoffer:好的,这就是我从线程池中了解到的。但是在一个请求数量很大的例子中(例如 10000),如果池中的线程数与可用线程数相关,这意味着非常高百分比的请求将一直排队?
  • 是的,然后他们将被排队(或者如果设置了这样的策略,甚至可能只是被拒绝)。但是,如果您预计有 10000 个并发请求并且有 50 个线程来处理它们,那么如果您需要及时处理请求,您可能需要一个更好的系统。
  • @Kristoffer:那么你所说的“更好的系统”是什么?它是根据所使用的硬件来定义的吗?既然在软件中除了使用线程来实现并发,还能做什么呢?
【解决方案2】:

还阅读 SEDA 模式 linklink

【讨论】:

    【解决方案3】:

    除了上面的答案,我应该注意到,具有许多传入连接的真正高性能服务器尝试不为每个连接生成一个线程,而是使用 IO 完成端口、select() 和其他异步技术来处理多个套接字在一个线程中。当然,必须特别注意确保一个请求或一个套接字的问题不会阻塞同一线程中的其他套接字。

    线程管理也会消耗 CPU 时间,因此不应为每个连接或每个客户端请求生成线程。

    【讨论】:

      【解决方案4】:

      在大多数系统中使用线程池。这是一个等待传入请求的可用线程池。线程数可以增加到配置的最大数量,具体取决于同时传入的请求数和应用程序的特性。

      如果有请求到达,则从线程池中请求一个未占用的线程。然后该线程专用于处理请求,直到请求完成。发生这种情况时,线程将返回到线程池以处理另一个请求。

      由于线程数量有限,因此在大多数服务器系统中,应尽量缩短请求的生命周期。请求需要执行的时间越短,线程就可以越早被重新用于新请求。

      如果在所有线程都被占用时请求进来,大多数服务器都会为请求实现排队机制。当然队列的大小也是有限的,所以当到达的请求多于排队时,新的请求将被拒绝。

      使用线程池而不是为每个请求启动线程的另一个原因是启动一个新线程是一项昂贵的操作。最好事先启动多个线程并重用它们,然后一直启动新线程。

      【讨论】:

      • 看线程池,我的理解是池中的线程数应该总是相对于可用处理器的数量。如果是这样,那么池中的线程数相对于请求数总是很低。如果我的理解是正确的,那么在我的示例中,假设有 48 个核心处理器的机器,应该只汇集 48 个线程,其余请求应该排队?我是在混淆这些概念吗?
      • 一个核心可以支持多线程。通常,一个线程不会做任何事情,因为它正在等待某些东西(IO,另一个线程,...)。如果一个 CPU 核心只支持一个线程,那么这个核心大部分时间都不会做任何事情。为了提高性能,您希望最大限度地延长核心工作时间,以便核心支持多个线程。
      • “一个核心可以支持多线程”这是困扰我的问题。如果我们只有 1 个核心和多个线程,那么这些多个线程将满足这 1 个核心。仅此一项不会降低性能吗?如果我对线程池的理解是正确的,那么线程池中应该有 1 个(核心)+1 个线程(即 2 个线程),其余的在排队?
      • 但是 CPU 在这方面非常擅长。一台机器通常运行多个使用多个线程的进程(即使你只运行操作系统),你甚至没有注意到这一点。当然,如果您有大量 CPU 密集型任务,争用可能会成为问题,但这种情况并不常见,因为很多时候线程都在等待某事。
      【解决方案5】:

      要让网络服务器处理大量并发连接,有几种方法(主要分为“每个连接一个线程”和“每个线程多个连接”类别),看看the C10K page,这是一个很棒的有关该主题的资源,讨论和比较了许多方法并链接到有关它们的更多资源。

      【讨论】:

        【解决方案6】:

        在大多数情况下,创建 10k 线程可能效率不高,但可以做到并且会奏效。

        如果您需要同时为 10,000 个客户提供服务,那么在单台机器上这样做是不可能的,但也是可能的。

        根据客户端的实现,可能10000个客户端不需要保持一个开放的TCP连接——根据目的,协议设计可以大大提高实现效率。

        我认为适用于大规模系统的解决方案可能非常针对特定领域,如果您需要建议,则必须详细说明您的问题领域。

        【讨论】:

          猜你喜欢
          • 2011-08-06
          • 2016-12-14
          • 2012-01-08
          • 1970-01-01
          • 2018-03-28
          • 2011-08-19
          • 2014-04-20
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多