多线程epoll服务器:唤醒休眠在同一个epoll fd上的N个线程

multithreaded epoll server: wake up N threads sleeping on the same epoll fd

我有一个多线程的 epoll 服务器。 我创建了一个 epoll fd,然后我将让 X 个线程休眠,等待 epoll_wait() 来自那个 SAME epoll fd.

的任何事件

现在我的问题是:如何唤醒 N 个线程,其中 N > 1 && N < X?

到现在为止,我一直在使用 Linux 特定的 eventfd 工具,它在只有 1 个线程的情况下工作得很好,但现在有多个线程在等待 SAME epoll fd,出现问题:

case 1) LT: 如果我用"level triggered"模式添加我的eventfd,当我写入eventfd时ALL线程会被唤醒,就是这样级别触发模式有效:一旦 fd 改变状态,我们就唤醒所有线程。

N = X

情况 2) ET:如果我用 "edge triggered" 模式添加我的 eventfd,当我写入 eventfd 时,只有 1 线程会被唤醒,这是边缘触发模式的工作原理:在我从 read(eventfd, ...);.

收到 EAGAIN 之前,不再有 epollfd 事件

N = 1

案例3)我也试过自管道技巧,向管道写入N次会唤醒N个线程。相反它不会工作:它不可靠,有时一个线程从管道读取 2 "tokens",有时 1 或 3.

N = 随机

在我尝试过的所有情况下,我不能只得到 N=N,我不能只唤醒 N 个线程,而是 1 或 ALL,或 RANDOM。 我错过了什么?有什么想法吗? 注意:我还尝试了 eventfd 特定的 EFD_SEMAPHORE 标志,没有任何帮助。

由于您主要是要退出唤醒的线程,正如您在评论中阐明的那样,您可以执行以下操作:

  • 使用ET,自管
  • 将数字N写入自管道(随便你,2或4字节的int)
  • 一个等待epoll_wait()的线程被唤醒,从管道中读取数字N,如果>1,将其递减并写入自管道,然后退出
  • 这会唤醒第二个线程,它从管道中读取数字(现在是 N-1)。如果它 > 1,则将其递减并写入自管道并退出。 ...

  • 以此类推,在某个时候最后一个线程从管道中读取 1。该线程应该退出但不再写入管道。

根据 eventfd 手册页。


The file descriptor is readable (the select(2) readfds argument; the poll(2) POLLIN flag) if the counter has a value greater than 0.

通过创建带有 EFD_SEMAPHORE 标志的 eventfd:

(if) the eventfd counter has a nonzero value, then a read(2) returns 8 bytes containing the value 1, and the counter's value is decremented by 1.


使用信号量(EFD_SEMAPHORE 标志),非阻塞 (EFD_NONBLOCK 标志)eventfd 并等待 level-triggered epoll(),或正常 poll().

eventfd_write(fd, N) 你写了 N 个线程 你想醒来。

当线程唤醒时,您执行 read()。如果 你得到 EAGAIN 错误,你可以回去睡觉了 因为 N 成功读取已经完成并且 因此 N 个线程知道它们必须保持清醒。

缺点

所有线程唤醒(thundering herd 问题)。