【发布时间】:2012-01-25 10:52:08
【问题描述】:
我正在用 c/c++ 编写一个兼容 POSIX 的多线程服务器,它必须能够异步接受、读取和写入大量连接。服务器有几个工作线程,它们执行任务,偶尔(并且不可预测地)将要写入套接字的数据排队。数据也偶尔(并且不可预测地)由客户端写入套接字,因此服务器也必须异步读取。这样做的一种明显方法是给每个连接一个线程,该线程从/向其套接字读取和写入;但是,这很丑陋,因为每个连接都可能持续很长时间,因此服务器可能必须保持数百或数千个线程才能跟踪连接。
更好的方法是让一个线程使用 select()/pselect() 函数处理所有通信。即,单个线程在任何套接字上等待可读,然后生成一个作业来处理输入,只要输入可用,该输入将由其他线程池处理。每当其他工作线程为连接产生输出时,它就会进入队列,并且通信线程在写入之前等待该套接字可写。
这样做的问题是,当输出被服务器的工作线程排队时,通信线程可能正在等待 select() 或 pselect() 函数。有可能,如果在几秒或几分钟内没有输入到达,则排队的输出块将等待通信线程完成 select()ing。但是,这不应该发生——数据应该尽快写入。
现在我看到了几个线程安全的解决方案。一种是让通信线程忙于等待输入并更新它等待每十分之一秒左右写入的套接字列表。这不是最佳选择,因为它涉及忙等待,但它会起作用。另一种选择是使用 pselect() 并在新输出排队时发送 USR1 信号(或等效的东西),允许通信线程立即更新它正在等待可写状态的套接字列表。我在这里更喜欢后者,但仍然不喜欢将信号用于应该是条件的东西(pthread_cond_t)。另一个选项是在 select() 正在等待的文件描述符列表中包含一个虚拟文件,每当需要将套接字添加到 select() 的可写 fd_set 时,我们都会向该文件写入一个字节;这将唤醒通信服务器,因为该特定的虚拟文件随后将是可读的,从而允许通信线程立即更新它的可写 fd_set。
我直觉地认为,第二种方法(使用信号)是对服务器进行编程的“最正确”方法,但我很好奇是否有人知道上述哪种方法最有效,一般来说,是否以上任何一种情况都会导致我不知道的竞争条件,或者如果有人知道这个问题的更通用的解决方案。我真正想要的是一个 pthread_cond_wait_and_select() 函数,它允许 comm 线程同时等待套接字的变化或来自条件的信号。
提前致谢。
【问题讨论】:
标签: c++ multithreading thread-safety pthreads asyncsocket