【问题标题】:Thread Pool Implementation线程池实现
【发布时间】:2011-03-30 17:59:48
【问题描述】:

我想创建一个简单的线程池(在 *nix 系统上)来处理异步通信服务器上的输入。我有一些问题。一开始我只是想拥有一个静态的线程数,随便说6个。

我应该如何将命令发送到工作线程?

我正在考虑使用简单的套接字通信,像这样

主线程::send [(recv) socket (send) otherSocket];

然后让我的线程简单地阻塞接收调用,这是非常低效的吗?

有我应该使用的库吗?有没有更好的实现方法?

(稍后我要让线程动态化,我只是想从一个设定的数字开始会更容易)

【问题讨论】:

    标签: c++ multithreading sockets asynchronous


    【解决方案1】:

    首先,您应该意识到 Windows 不仅内置了一个,而是 两个 内置线程池 API。如果这还不够,I/O 完成端口也基本上是线程池变相。因此,可能根本没有太多理由编写自己的线程池。

    除非您打算让您的线程在不同的机器上运行,否则我不会使用套接字进行通信。线程的正常意图是它们应该将开销保持在最低限度。插座不太适合那个。如果你跨越机器边界,更多的开销(网络通信)变得几乎不可避免,在这种情况下,使用套接字而不是其他一些网络 API 并没有太大的区别。另请注意,对于跨机器工作,您通常需要做更多的工作,将数据从一个到另一个编组,等等。

    假设您只使用一台机器,我会使用某种线程安全队列而不是套接字。我在previous answer 中发布了一个您可能会觉得有用的帖子。其中包括一些创建线程池以服务队列中的任务的示例代码。

    【讨论】:

    • 我的服务器机器将是一台linux(可能是ubuntu服务器)机器,你知道是否有可用的线程池API吗?如有必要,我可以使用 windows,但由于缺少 poll() 函数,我宁愿远离... *** 我将它保存在一台机器上。我喜欢你的队列实现,我会试一试。 *** 感谢您的回复。
    • @ultifinitus:这个特殊的队列实现使用了 Windows 函数,所以它不能在 Linux 上运行。您希望/需要用 pthreads 等效项替换 Windows 函数调用(这并不完全简单)。 Boost 有一个 C++“futures”实现,您可能会觉得它很有用(它基本上是线程池的高级 API)。我不确定,但我似乎记得一些最新版本的 gcc 在标准库中有未来(它们是新 C++11 标准的一部分)。
    • 我想我的计划,在这一秒是做一个非常简单的线程池,如果没有别的,看看我微薄的技能会带来什么性能提升。 *** 我将研究未来 *** 我在 Windows 和 Posix 系统中都有使用线程的经验。 *** 现在我只是要创建一个管理器类,所以当我的服务器收到一条消息时,它可以将它扔给可以将结果分配给我的线程的管理器......再次感谢。
    【解决方案2】:

    有你应该使用的库吗?好吧,最肯定的是,否则您将无法在标准 C++ 中完成您想要的任何事情。这带来了您想要使用什么平台的问题……您使用的是 Windows 还是某种 Linux?如果您使用的是 Windows,您可以使用 Windows 线程和 Win Socks 2。如果您使用的是 Linux,您可以尝试 Posix 线程,至于网络我不太确定。但是,您可以只使用 Boost 库来完成所有这些工作,它应该可以跨 Windows 和您的 Linux 操作系统工作。

    就发送消息而言,您可以为消息队列使用互斥锁,只需使用接收消息函数将其锁定并添加消息,该函数适用于任何线程上来自套接字的联网传入消息,对于其他想要向该线程发送消息的线程。另外别忘了,当线程在处理消息时,你也要锁定互斥锁;每当您访问该消息队列时,都需要将其锁定以防止冲突/数据错误。

    【讨论】:

    • 它很可能会在一个 posix 兼容的系统上。抱歉没有马上说明,我会继续编辑。
    【解决方案3】:

    由于您使用的是套接字,因此您必须使用诸如 select 或 poll 之类的东西来等待套接字活动。因此,对于线程间通信 (itc),使用文件描述符的东西将非常有用。这可能是一对或管道或 unix 域套接字。

    然后您的主线程将接受一个新的连接并使用您的 itc 机制将新的文件描述符传递给工作线程。然后工作线程会将新的文件描述符添加到选择/轮询循环中。

    如果您正在连接并等待后端服务器(如数据库),您可能会在等待 recv 完成时决定阻塞,尽管您可能应该避免这种情况。如果您使用状态机并将堆栈卷回选择外观(在将后端文件描述符添加到选择/轮询循环之后),您的服务器将更加响应。这将使您的主线程更容易控制您的工作线程。如果您很聪明,您的线程可能一次能够处理多个请求,并且只有少数线程,您应该拥有一个非常可扩展的服务器。

    尽量避免对 itc 使用信号量或互斥体,而是通过您的 itc 管道或 unix 域套接字发送所有请求。比赛条件会让您省去很多心痛。

    【讨论】:

    • 我在系统上使用 poll 来接收主线程上的输入,我正在考虑简单地等待 poll,然后在特定线程中抛出文件描述符。我希望我的服务器在任何给定时间处理超过 1000 个客户端,你知道这个系统的可扩展性吗?
    • 然后我会尽量避免每个会话使用一个线程,而是每个线程有多个会话。诀窍是确保线程仅在轮询时阻塞。多线程的原因就变成了确保所有 CPU 内核始终被占用。如果您的系统中仍然没有足够的果汁,您可能需要考虑使用多台服务器进行某种负载平衡。