多线程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 问题)。
我有一个多线程的 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 问题)。