这是一个普遍的问题 - 但是我已经多次遇到过这个问题,但我仍然没有找到最好的解决方案。
让我们假设你有一个多线程的程序(例如HTTP应用程序服务器),它通过套接字(TCP,Unix,...)进行通信。主线程使用异步IO和select()或poll()POSIX调用来从/向套接字分派流量。还有工作线程处理请求并提供响应。为了将响应发送回客户端,工作线程与主线程(轮询)以某种方式同步。问题的核心是“如何” - 就效率而言。我可以使用基于管道() - 套接字的IPC机制 - 但在我看来这是一个非常巨大的开销。我倾向于使用一些pthread IPC技术,如互斥,条件变量等......但这些不适用于select()或poll()。
POSIX(及其周围环境)中是否存在解决此冲突的常用技巧? 我想在Windows上有WaitForMultipleObjects()函数允许这样做。
示例程序是为了说明一个问题,我知道我可以用不同的方式设计主/模式,但这不是我要求的。我还有其他情况,我处于相同的情况。
答案 0 :(得分:5)
您可以使用信号来戳工作线程,这将中断select()
调用并返回EINTR
。使用pselect()
可以更轻松地完成此操作。
为此起作用:
pselect()
中的信号掩码参数在等待时解锁信号。在线程之间,您可以使用pthread_kill
专门向工作线程发送信号。当另一个进程应该发送信号时,您可以确保除了工作线程之外的所有信号都被阻塞(因此它将被传送到那里),或者使用信号处理程序来确定信号是否被发送到工作线程,并使用pthread_kill
显式转发它(工作线程仍然不需要在信号处理程序中做任何事情)。
由于我的懒惰,我没有在线的源代码查看器,但您可以克隆LibreVISA git树,并查看src/messagepump.cpp
,其中使用此方法在另一个线程将一个文件描述符添加到监视列表之后戳戳工作线程。
答案 1 :(得分:3)
我的理解是:
为什么不通过使工作线程负责来自特定连接的所有事务来消除必须在工作线程和主线程之间进行同步的问题? 因此,主线程仅负责监听新连接并使用连接信息(即新连接的文件描述符)启动工作线程。
答案 2 :(得分:1)
首先,唤醒另一个线程的方法是使用线程A中的pthread_cond_wait
/ pthread_cond_timedwait
调用来等待,并使线程B使用pthread_cond_broadcast
/ {{1捡起来。因此,例如,如果B是生产者而A是消费者,则生产者可能会将项目添加到受互斥锁保护的链接列表中。会有一个关联的条件变量,这样在添加项目之后,它可以唤醒线程B,以便它查看是否有任何新项目已经到达列表,如果已经删除它们。我说'关联'因为那时相同的互斥锁可以与条件变量相关联,以保护列表。
到目前为止一切顺利。现在提到异步I / O.对于一组FD 和一组条件变量,我想要做多次的pthread_cond_signal
或select()
是poll()
,当条件变量被广播时,select()
被中断。没有简单的方法直接这样做;你不能简单地混合搭配。
因此,你需要做两件事之一。之一:
解决问题(例如,使用自连接poll()
发送一个字节来唤醒pipe()
而不是条件变量,以及条件变量,或来自等待条件变量的其他一些线程;或
转换为更多线程的模型。 IE使用一个线程进行发送,一个线程用于接收,并使用生产者/消费者模型,因此发送方线程只是从列表/缓冲区中删除并发送(必要时阻塞),并且接收到等待I / O(阻塞,如果必要的)并将其添加到列表中(这是您在结尾处用斜体字表示的内容)。
第二个是我们这些在异步I / O上工作的主要设计变化,第一个是丑陋的。你不是第一个对此感到沮丧的人,但我找不到一个简单的方法。第一个效率低下,如果你只写一个字符来唤醒选择循环到自我管道,我不认为你会看到太多的低效率。