我正在用c/c++编写一个符合POSIX标准的多线程服务器,它必须能异步接受、读取和写入大量连接。该服务器有多个工作线程执行任务,并且偶尔(且不可预测地)将数据排队以写入套接字。客户端也会偶尔(且不可预测地)向套接字写入数据,因此服务器也必须异步读取。一个明显的方法是为每个连接分配一个线程,该线程从其套接字读取和写入数据;但是,这种方式很丑陋,因为每个连接可能会持续很长时间,因此服务器可能需要保持数百或数千个线程来跟踪连接。
更好的方法是使用select()/pselect()函数处理所有通信的单个线程。即,单个线程等待任何套接字变为可读状态,然后生成一个任务以处理输入,该任务将在其他工作线程池中处理每当输入可用时。每当其他工作线程为一个连接产生输出时,它会被排队,通信线程将在可写时等待该套接字,然后进行写操作。
问题在于,当服务器的工作线程排队输出时,通信线程可能在select()或pselect()函数中等待。如果几秒钟或几分钟内没有输入到达,则排队的输出将等待通信线程完成select()。然而,这不应该发生--数据应尽快写入。
目前我看到有几种线程安全的解决方案。其中一种是让通信线程在输入上忙等待并更新它每秒钟左右等待写入的套接字列表,这并不是最优的选择,因为它涉及到忙等待,但它可以工作。另一个选择是使用pselect(),并在新输出排队时发送USR1信号(或类似信号),允许通信线程立即更新它等待可写状态的套接字列表。我更喜欢后者,但仍然不喜欢将信号用于应该是条件(pthread_cond_t)的东西。还有另一个选择是在select()等待的文件描述符列表中包括一个虚拟文件,当需要添加套接字到可写fd_set以进行select()调用时,我们向其写入单个字节;这将唤醒通信服务器,因为特定虚拟文件会变成可读状态,从而允许通信线程立即更新可写fd_set。
直观上,我感觉第二种方法(使用信号)是编写服务器的“最正确”方法,但我很好奇是否有人知道上述哪种方法通常最有效,是否会导致我不知道的竞态条件,或者是否有人知道此问题的更通用解决方案。我真正想要的是一个pthread_cond_wait_and_select()函数,它允许comm线程等待套接字变化或条件信号。
提前感谢您的帮助。