【问题标题】:How does a thread pool allows me to handle many client connections?线程池如何让我处理许多客户端连接?
【发布时间】:2015-04-21 20:30:10
【问题描述】:

我想处理 300 到 400 个客户端连接,但我不想为每个客户端连接创建一个线程(或者创建 400 个线程有什么问题吗?)。

所以我读到我应该使用线程池来解决这个问题,但我无法理解线程池是如何真正解决这个问题的。我的意思是在我对线程池的理解中,开始执行任务的线程数量是有限的。但是一旦线程接受recv() 任务,如果没有可读取的内容,它将立即阻塞!那么解决方案不应该是我应该有一种机制,让我在实际尝试阅读之前知道是否有要阅读的东西吗?那么线程池究竟是如何解决我处理大量客户端连接的问题的呢?


编辑:read()更改为recv()

【问题讨论】:

标签: c++ windows multithreading sockets network-programming


【解决方案1】:

正如 user743414 已经指出的那样,多线程不是一个好主意。但主要问题在于恕我直言,您阻止read。仅当有要阅读的内容时才应使用 read。使用select 找出哪个套接字有要读取的内容并将其分派到线程池之外的工作线程是通常的方法。 对于 Windows,您应该使用 WSASockets。

您在单个线程中使用select。比您使用select 的结果(它将告诉您需要在哪个套接字上执行操作)将连接分派到工作线程。

你写道你使用微软。取样:

https://msdn.microsoft.com/en-us/library/windows/desktop/ms742219(v=vs.85).aspx

搜索代码

    //-----------------------------------------
    // If data has been received, echo the received data
    // from DataBuf back to the client
    iResult =
        WSASend(AcceptSocket, &DataBuf, 1, &RecvBytes, Flags, &AcceptOverlapped, NULL);
    if (iResult != 0) {
        wprintf(L"WSASend failed with error = %d\n", WSAGetLastError());
    }

你可以用你的线程池替换这部分,比如(伪代码):

mythreadpool *thread=takeOrCreateThreadFromThreadPool();
thread->callWith(&DataBuf,&RecvBytes);

您会发现许多不同但很好的线程池实现将使用类似的方法。

【讨论】:

  • 所以线程池通常和select()一起使用?我认为select() 和线程池是两种不同的方法!我还读到select() 只能处理有限数量的套接字。
  • 对不起,我在问题中写了“read()”,我的意思是“recv()”(但我认为它们的意思是一样的)。如果我不太关心性能,我可以在没有线程池的情况下使用select()(我没有来自客户端的恒定数据,只有在很长的时间间隔内我才有数据到达)?
  • @user4582812 读取没问题:-)。是的当然。线程池增加了很多复杂性,你应该避免它们。如果您没有复杂的计算或 DB 请求会阻塞您的程序,则不应使用多线程。
【解决方案2】:

线程池很有帮助,因为您可能不会拥有所有 400 个连接不断发送和接收数据,因此您的应用只需要少数线程来管理它们。

单个线程可以监视所有连接(例如使用select),一旦选择解锁,它就会循环遍历所有需要注意的套接字并将它们传递给线程池。如果select 指定一个sockets 已经接收到数据,那么read 将不会阻塞(你仍然可以将读取的超时时间设置为0)

【讨论】:

  • 所以线程池通常和select()一起使用?我认为select() 和线程池是两种不同的方法!我还读到select() 只能处理有限数量的套接字。
  • @user4582812 select 可以在一个线程中运行,因为它所做的只是等待数据到达:当数据到达时 read() 在另一个线程中执行,因为它可能涉及一些繁重的工作(例如解析数据,将其存储在某处等)。 FD_SET 的最大文件描述符数在导入 winsock2.h 之前是可配置的
【解决方案3】:

创建 300 - 400 个线程应该可行,但不是最佳解决方案。上下文切换是您必须搜索的关键字。上下文切换很昂贵。 更多线程的另一个问题是每个线程获得 1 MB 堆栈内存,并且该内存是有限的。您可以轻松地尝试一下,检查您可以创建多少线程。

使用线程池,您有一个线程接收请求,然后将这些请求提供给您的线程池以工作。 所以你不会有一个线程在等待读取时阻塞。你的线程池只是在有东西要读的时候工作。

另一个更好的选择是在 Windows I/O 完成端口上。类似的技术也可以在 linux 上使用。

【讨论】:

  • 那么有一种机制可以让线程池在尝试读取之前知道有东西要读取?
  • 像其他用户写的一样。您可以在单个线程中使用 select(),然后将某些内容“发布”到您的线程以进行处理。
  • Windows 上的首选方式是相反的:启动一些重叠读取并阻塞完成端口,以便在读取完成时收到通知。这适用于一个线程或一个线程池,并且可以扩展到数万个套接字。在类 Unix 系统下,您将首先 poll(或 select)是否有一个套接字准备好,然后从中读取。在 Windows 下工作,但不是很好。
猜你喜欢
  • 2012-08-27
  • 2018-04-28
  • 2018-12-04
  • 2011-03-28
  • 1970-01-01
  • 2020-07-18
  • 1970-01-01
  • 1970-01-01
  • 2017-08-04
相关资源
最近更新 更多