C++ 线程之间的可轮询信令
我正在从事一个项目,其中主服务器线程需要将事件分派给一系列工作线程。在工作线程中进行的工作依赖于轮询(即,epoll或kqueue,具体取决于所讨论的UNIX系统),需要处理这些操作的超时。这意味着,正常的条件变量或信号量结构不适用于此分派,因为它会造成一个或另一个块,从而在处理来自轮询的事件或来自服务器线程的事件之间产生不必要的延迟 因此,我想知道以可轮询方式在线程之间调度此类事件的最佳构造是什么?本质上,需要传递的只是一个可轮询的“信号”,它告诉工作线程它有更多的事件要获取。我已经研究过使用UNIX管道(未命名管道,因为它是进程的内部管道),这似乎是一个不错的解决方案,因为一个字节可以写入管道,并在队列被清除时读取——但是,我想知道这是否是可用的最佳方法?还是最快的C++ 线程之间的可轮询信令,c++,c,unix,C++,C,Unix,我正在从事一个项目,其中主服务器线程需要将事件分派给一系列工作线程。在工作线程中进行的工作依赖于轮询(即,epoll或kqueue,具体取决于所讨论的UNIX系统),需要处理这些操作的超时。这意味着,正常的条件变量或信号量结构不适用于此分派,因为它会造成一个或另一个块,从而在处理来自轮询的事件或来自服务器线程的事件之间产生不必要的延迟 因此,我想知道以可轮询方式在线程之间调度此类事件的最佳构造是什么?本质上,需要传递的只是一个可轮询的“信号”,它告诉工作线程它有更多的事件要获取。我已经研究过使用
或者,也可以在Linux上使用signalfd(2),但由于这在BSD系统上不可用,我宁愿避免这种构造。我还想知道使用系统信号的开销到底有多大?就性能而言,系统调用的成本与其他操作相比是相当巨大的,因此重要的是系统调用的数量。有两种选择:
select
和poll
具有变量,它们也会等待信号(pselect
,ppoll
)。Linuxepoll
可以使用signalfd
执行相同的操作,因此kqueue
是否可以等待信号仍然是一个问题,我不知道。如果可以,您可以使用它们(无论如何,您在Linux和*BSD上使用的是不同的机制)。如果您没有很好地使用传递的数据,它将为您节省用于读取的系统调用我希望通过套接字传递数据会更有效,如果它允许您取消任何其他锁定。Jan Hudec的回答是正确的,尽管出于以下几个原因,我不建议使用信号:
- glibc的旧版本以非原子方式模拟了
和pselect
,使它们基本上一文不值。即使正确使用掩码,在ppoll
和pthread\u sigprocmask
调用之间,信号也可能“丢失”,这意味着它们不会导致select
EINTR
- 我不确定
是否比管道更有效。(我还没有测试过它,但我没有任何特别的理由相信它是。)signalfd
- 信号通常是一种很难纠正的问题。我在这些问题上花了很多精力(请参阅),如果可以的话,我建议您避免使用它们
epoll
或kqueue
,甚至在您添加新事件时代表您唤醒工作人员。看
而且
工作线程处理套接字I/O和异步磁盘I/O,这意味着它最好总是等待事件队列机制(epoll/kqueue)
你在这里可能会感到失望。这些事件队列机制实际上不支持异步磁盘I/O。有关更多详细信息,请参阅。线程将从何处获取数据?如果我误解了这个问题,我很抱歉,但是通过轮询(2)调用简单地轮询线程将读取的任何输入源有什么错呢?我重新阅读了您的问题几次,但我仍然对您的问题有点模糊。。。听起来工作线程有一些死区时间(等待管道或其他东西),并且。。。那就是我迷路的地方。为什么死亡时间是相关的?你不想等他们完成他们的工作吗?显然我的解释被删除了,就这样。工作线程处理套接字I/O和异步磁盘I/O,这意味着它最好总是等待事件队列机制(epoll/kqueue)。问题是,新任务也会被传递给线程,但由于这些任务不一定依赖于I/O,我不能简单地将它们扔到特定工作线程的事件循环中。因此,我必须找到一种方法来通知这个事件轮询循环中的工作人员,新的应用程序事件可以处理。否则我就浪费时间了。正如你所想的,使用管道是一个不错的选择。您只需将其读取端添加到正在等待的文件描述符组中即可。感谢您在“信号与管道”决策中的输入。就轮询机制而言,我从一开始就实现了这些机制,以避免使用POSIX AIO库,所以这没什么大不了的,但首先要感谢您——尽管以我的经验来看,对于特定的事情来说,这会更好一些。谢谢!
2058 static inline int
2059 event_add_internal(struct event *ev, const struct timeval *tv,
2060 int tv_is_absolute)
2061 {
...
2189 /* if we are not in the right thread, we need to wake up the loop */
2190 if (res != -1 && notify && EVBASE_NEED_NOTIFY(base))
2191 evthread_notify_base(base);
...
2196 }