我正在开发一个项目,主服务器线程需要将事件分派给一系列工作线程。工作线程中的工作依赖于轮询(即epoll或kqueue,具体取决于所讨论的UNIX系统),这些操作需要超时才能处理。这意味着,正常的条件变量或信号量结构对于此调度是不可行的,因为它会使一个或另一个块在处理来自轮询的事件或源自服务器线程的事件之间导致不希望的延迟。 / p>
所以,我想知道以可轮询方式在线程之间调度此类事件的最佳构造是什么?从本质上讲,所有需要传递的是一个可轮询的“信号”,告诉工作线程,它有更多的事件要获取。我已经看过使用UNIX管道(未命名的,因为它是进程内部的),这似乎是一个不错的解决方案,因为可以将单个字节写入管道并在清除队列时读回 - 但是,我我想知道这是否是最好的方法?还是最快的?
或者,可以在Linux上使用signalfd(2),但由于这在BSD系统上不可用,我宁愿避免使用这种结构。我也想知道实际使用系统信号的开销有多大?
答案 0 :(得分:1)
就性能而言,系统调用的成本与其他操作相比相当巨大,因此重要的是系统调用的数量。有两种选择:
select
和poll
有变体,也等待信号(pselect
,ppoll
)。 Linux epoll
可以使用signalfd
执行相同操作,因此kqueue
是否可以等待信号仍然是一个问题,我不知道。如果它可以,你可以使用它们(你在Linux和* BSD上使用不同的机制)。如果您没有很好地使用传递的数据,它将为您节省读取的系统调用。如果允许你取消任何其他锁定,我希望通过套接字传递数据会更有效。
答案 1 :(得分:1)
Jan Hudec的回答是正确的,虽然我不建议使用信号有几个原因:
pselect
和ppoll
,使它们基本上毫无价值。即使您正确使用了遮罩,信号也可能会在pthread_sigprocmask
和select
来电之间“丢失”,这意味着它们不会导致EINTR
。signalfd
是否比管道更有效率。 (没有测试过,但我没有任何特别的理由相信它。)由于您尝试将异步处理移植到多个系统,我建议您查看libevent。它将为您抽象epoll
或kqueue
,它甚至会在您添加新事件时代表您唤醒工作人员。见event.c
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 }
此外,
工作线程处理套接字I / O和异步磁盘I / O,这意味着它最好总是等待事件排队机制(epoll / kqueue)。
你可能会在这里感到失望。这些事件排队机制并不真正支持异步磁盘I / O.有关详细信息,请参阅this recent thread。