线程池如何让我处理许多客户端连接?
How does a thread pool allows me to handle many client connections?
我想处理 300 到 400 个客户端连接,但我不想为每个客户端连接创建一个线程(或者创建 400 个线程有什么问题吗?)。
所以我读到我应该使用线程池来解决这个问题,但我无法理解线程池实际上是如何解决这个问题的。我的意思是在我对线程池的理解中,开始接任务的线程数量是有限的。但是一旦一个线程接受了一个 recv()
任务,如果没有什么可读的,它会立即阻塞!那么解决方案不应该是我应该有一种机制,让我在实际尝试阅读之前知道是否有东西要阅读吗?那么线程池究竟是如何解决我处理很多客户端连接的问题的呢?
编辑: 将 read()
更改为 recv()
。
创建 300 - 400 个线程应该可行,但这不是最佳解决方案。上下文切换是您必须搜索的关键字。上下文切换很昂贵。
更多线程的另一个问题是每个线程获得 1 MB 堆栈内存,并且该内存是有限的。您可以轻松地尝试这个检查您可以创建多少线程。
使用线程池,您有一个线程接收请求,然后将这些请求交给您的线程池工作。
所以你不会有一个线程在等待读取时阻塞。你的线程池只是在有东西要读的时候才工作。
另一个更好的选择是 Windows I/O 完成端口。 linux.
上也提供类似技术
正如 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);
您会发现许多不同但很好的线程池实现,它们将使用类似的方法。
线程池很有帮助,因为您可能不会让所有 400 个连接不断发送和接收数据,因此您的应用程序只需要少数几个线程来管理它们。
单个线程可以监视所有连接(例如使用 select
),一旦 select 解锁,它就会循环遍历所有需要注意的套接字并将它们传递给线程池.如果select
指定一个sockets已经接收到数据那么read
不会阻塞(你仍然可以将读取超时设置为0)
我想处理 300 到 400 个客户端连接,但我不想为每个客户端连接创建一个线程(或者创建 400 个线程有什么问题吗?)。
所以我读到我应该使用线程池来解决这个问题,但我无法理解线程池实际上是如何解决这个问题的。我的意思是在我对线程池的理解中,开始接任务的线程数量是有限的。但是一旦一个线程接受了一个 recv()
任务,如果没有什么可读的,它会立即阻塞!那么解决方案不应该是我应该有一种机制,让我在实际尝试阅读之前知道是否有东西要阅读吗?那么线程池究竟是如何解决我处理很多客户端连接的问题的呢?
编辑: 将 read()
更改为 recv()
。
创建 300 - 400 个线程应该可行,但这不是最佳解决方案。上下文切换是您必须搜索的关键字。上下文切换很昂贵。 更多线程的另一个问题是每个线程获得 1 MB 堆栈内存,并且该内存是有限的。您可以轻松地尝试这个检查您可以创建多少线程。
使用线程池,您有一个线程接收请求,然后将这些请求交给您的线程池工作。 所以你不会有一个线程在等待读取时阻塞。你的线程池只是在有东西要读的时候才工作。
另一个更好的选择是 Windows I/O 完成端口。 linux.
上也提供类似技术正如 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);
您会发现许多不同但很好的线程池实现,它们将使用类似的方法。
线程池很有帮助,因为您可能不会让所有 400 个连接不断发送和接收数据,因此您的应用程序只需要少数几个线程来管理它们。
单个线程可以监视所有连接(例如使用 select
),一旦 select 解锁,它就会循环遍历所有需要注意的套接字并将它们传递给线程池.如果select
指定一个sockets已经接收到数据那么read
不会阻塞(你仍然可以将读取超时设置为0)