2011-09-12 32 views
2

我正在处理一个项目,其中主服务器线程需要将事件分派到一系列工作线程。在工作线程中进行的工作依赖于轮询(即根据所讨论的UNIX系统的epoll或kqueue),这些操作需要处理时超时。这意味着一个正常的条件变量或信号量结构对于这个分派是不可行的,因为它会使得一个或另一个分组导致处理来自轮询的事件或源自服务器线程的事件之间的不希望的等待时间。线程之间的可轮询信号

所以,我想知道什么最优化的构造以可调节的方式在线程之间分派这样的事件是?实质上,所有需要传递的信息都是一个可信的“信号”,告诉工作者线程,它有更多的事件要获取。我研究过使用UNIX管道(未命名的,因为它在进程内部),这似乎是一个体面的解决方案,因为可以将单个字节写入管道并在队列清除时读回 - 但是,我想知道这是否是最好的方法?还是最快?

或者,可以在Linux上使用signalfd(2),但由于BSD系统上没有此功能,所以我宁愿避免这种构造。我也想知道使用系统信号的开销实际上有多大?

+0

我重读了你的问题几次,我对你的问题仍然有点模糊......这听起来像工作线程有一些死亡时间(等待一个管道或什么的)和...这是我迷路了。为什么死亡时间是相关的?不是你只是想等他们完成他们的工作吗? – Owen

+0

线程从哪里获取数据?如果我误解了这个问题,我很抱歉,但是,如果只是轮询线程读取poll(2)调用的任何输入源会出现什么问题? –

+0

显然我的解释刚刚被删除,所以在这里。工作线程处理套接字I/O和异步磁盘I/O,这意味着它始终等待事件排队机制(epoll/kqueue)。问题是,新任务也交给了线程,但由于这些任务不一定依赖于I/O,我不能简单地将它们引入特定工作线程的事件循环中。 因此,我必须找到一种方法来在此事件轮询循环中通知工作人员,以确保新的应用程序事件可以处理。否则,我会浪费时间在其中一方。 –

回答

1

扬邬达克的答案是正确的,虽然我不建议使用的信号有以下几个原因:的glibc

  • 旧版本以非原子方式模拟pselectppoll,使他们基本上是毫无价值的。即使正确使用了面罩,信号也可能在pthread_sigprocmaskselect调用之间“丢失”,这意味着它们不会导致EINTR
  • 我不确定signalfd比管道更有效率。 (还没有测试过,但我没有任何特别的理由相信它。)
  • 信号通常是一个很难找对的问题。我花了很多精力(见my sigsafe library),如果可以的话,我建议避开它们。

由于您试图将异步处理移植到多个系统,因此我建议您查看libevent。它会为您抽象epollkqueue,甚至会在您添加新事件时代表您唤醒工作人员。见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

+1

感谢信号与管道决策的输入。在投票机制方面,我已经从头开始实施它们,以免使用POSIX AIO库,所以这并不是什么大问题,但是要感谢你们 - 尽管根据我的经验,[libev](http:// software.schmorp.de/pkg/libev.html)对于特定的东西有点更好。非常感谢! –

1

就性能而言,系统调用的成本与其他操作相比是相当巨大的,所以系统调用的数量就很重要。有两种选择:

  1. 按照您的要求使用管道。如果您有任何有用的消息有效负载,您将获得一个系统调用发送,一个系统调用等待和一个系统调用接收。尝试将任何相关数据传递给管道,而不是从共享结构中读取它们以避免额外的锁定开销。
  2. selectpoll有变种,也等待信号(pselect,ppoll)。 Linux epoll可以使用signalfd来做同样的事情,所以kqueue是否可以等待我不知道的信号仍然是个问题。如果可以的话,你可以使用它们(无论如何,你在Linux和* BSD上都使用不同的机制)。如果您对传递的数据没有很好的使用,它将为您节省读取的系统调用。

我希望通过套接字传递数据更有效率,如果它允许你消除任何其他锁定。