use*_*198 9 c++ multithreading pthreads thread-safety asyncsocket
我正在用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线程等待套接字的更改或来自条件的信号.
提前致谢.
这是一个相当普遍的问题.
一种常用的解决方案是将管道作为从工作线程返回I/O线程的通信机制.完成其任务后,工作线程将指向结果的指针写入管道.I/O线程在管道的读取端与其他套接字和文件描述符一起等待,一旦管道准备好读取它就会唤醒,检索指向结果的指针并继续将结果推送到非客户端连接中阻塞模式.
注意,由于管道读取和写入小于或等于PIPE_BUF原子,指针一次写入并读取.由于原子性保证,甚至可以让多个工作线程将指针写入同一个管道.