提问人:nick2225 提问时间:10/3/2023 最后编辑:nick2225 更新时间:10/3/2023 访问量:128
使用线程池和多个事件的 Linux 轮询
Linux poll with a thread pool and multiple events
问:
我正在用 C 语言开发一个简单的客户端服务器程序,其中多个客户端将连接到单个服务器。
客户端将向服务器提交操作/操作,服务器将处理这些请求。这些操作可能很昂贵和/或运行时间很长,所以理想情况下,我希望在服务器上有一个可以并发处理请求的线程池,而不是阻塞主线程。
此外,我还认为使用(不能使用,因为我需要符合 POSIX 标准)可能比为每个套接字连接创建一个新线程(源于 C10K 问题:https://en.wikipedia.org/wiki/C10k_problem)可能更好。poll
epoll
因此,从理论上讲,服务器可能看起来像以下伪代码
int main()
{
// Pretend these are initialized in some manner
ThreadPool thread_pool;
Socket server_listening_socket;
PolledFileDescriptors list_of_polled_fds;
// The first pollfd will be the listening socket which looks for read events on it
list_of_polled_fds[0].fd = server_listening_socket;
list_of_polled_fds[0].events = POLLIN;
while (true)
{
// Call poll on our list of file descriptors with unlimited timeout (-1)
poll(&list_of_polled_fds, number_of_fds, -1);
for (int i = 0; i < number_of_fds; i++)
{
// We received a read event on this file descriptor
if (list_of_polled_fds[i].revents & POLLIN)
{
// The listening socket has an event (meaning a new connection was created)
if (i == 0)
{
Socket client_socket = accept();
AddClientConnectionToListOfPollFds(&list_of_polled_fds, client_socket);
}
// A connected client has an event (data was sent over the socket)
else
{
ThreadPoolTask task = {
.argument = list_of_polled_fds[i].fd // client connected file descriptor
.function = SomeFunctionToReadDataFromSocketAndProcessIt
};
AddTaskToThreadPool(&thread_pool, &task);
}
}
}
}
return 0;
}
现在有了这个高级设计,我有一些担忧。
单个消息导致多个事件
- 假设客户端尝试向服务器发送一条 10 字节的消息,但由于某种原因,字节被拆分为 2 个 TCP 数据包。
- 第一个数据包将进入客户端套接字,这将导致检测到事件。
poll
- 然后,它将此套接字放入线程池上的任务中,该任务将读取和处理数据。
- 然后,第二个数据包进入并导致轮询执行相同的操作。
- 现在我的线程池中有 2 个任务,它们对应于同一个套接字,并且应该是相同的“消息”。
我应该如何管理?我是否应该只跟踪线程池中当前正在处理哪些套接字,而如果存在任务,则不添加相同的套接字?
如果我保护线程池不两次添加相同的套接字,那么这意味着如果单个客户端发送 2 个独立的请求,我将无法并行处理它们。我将不得不等待第一条消息完成,然后处理下一条消息。
什么是好的机制来检测多个事件是否属于单个客户端消息,这样我既不能将冗余任务添加到线程池,又可以同时处理来自客户端的多个请求?poll
答: 暂无答案
评论
recv()
EAGAIN
EWOULDBLOCK
recv
EWOULDBLOCK
EAGAIN