eventfd & epoll & 消费者线程池

在Linux系统中,eventfd 是一个用来通知事件的文件描述符,timerfd 是定时器事件的文件描述符。二者都是内核向用户空间的应用发送通知的机制,可以有效地被用来实现用户空间的事件/通知驱动的应用程序。

简而言之,就是 eventfd 用来触发事件通知,timerfd 用来触发将来的事件通知。

当仅用于实现信号通知的功能时,eventfd() 完全可以替代 pipe(),对于内核来说,eventfd 的开销更低,消耗的文件描述符数目更少(eventfd 只占用一个描述符,而 pipe 则需要两个)。

eventfd 的两个核心操作

  1. read()

消费者需要对信号量进行 down 操作时,调用 read 从 eventfd 读即可。read返回值:

如果当前 counter > 0,那么 read 返回 counter 值,并重置 counter 为0;

如果当前 counter 等于0,那么 read 阻塞直到 counter 大于0;如果设置了NONBLOCK,那么返回-1,并设置 errno 为EAGAIN。
可以看到,eventfd 实现的资源是一次性消耗品,只允许一次 read。

  1. write()

生产者需要执行 up 操作时,调用 write 写一个 64bit 的整数 value 到 eventfd 即可。write 返回值:

counter 最大能存储的值是 0xffff ffff ffff fffe(以max表示此值),那么 write 尝试将 value 加到 counter上,如果结果超过 max,那么 write 一直阻塞直到有 read 操作发生,或者返回-1并设置 errno 为 EAGAIN。

所以 write 可以多次连续调用,但 read 读一次即可清零。实质上它应该是一个二元信号量,只有0和非0两种状态。但是应用程序也可以利用 counter 的值实现自己的逻辑,比如每次 write 都加 1,那么 read 就能够知道在两次调用之间有多少次 write 操作发生,也就表示对应的事件发生了多少次。

在这里插入图片描述
消费者

消费者线程池中的线程和生产者共用一个 epoll 对象,每个消费者线程并行地进行针对 eventfd 或 timerfd 触发的事件循环的轮询。如果是读事件,且事件 ID 为注册过的 eventfd 或 timerfd,则进行相应的处理。然后执行 eventfd/timerfd 的 read() 操作(相当于 down),并注销执行完的 eventfd 或 timerfd。

创建消费者线程池:

class EventPool(object):
    def __init__(self, name, num_threads=4):
        super(EventPool, self).__init__()
        self.name = name
        self._epoll = select.epoll()
        self._events = {}
        for _ in range(num_threads):
            w = EpollThread(self.name, self._epoll, self._events, self._attach_event)
            w.start()

消费者线程执行:

def run(self):
        while True:
            events = self._epoll.poll(1)
            for fd, event in events:
                if fd in self._events and event & select.EPOLLIN:
                    self._exec_handler(self._events[fd])
                    self._events[fd]['fd'].read()
                    self._events.pop(fd)

生产者

创建 eventfd/timerfd 并在 epoll 对象中注册事件,然后执行 eventfd/timerfd 的 write() 操作(相当于 up)。

if expire is not None:
	tfd = TimerFd()
    v = max(1e-3, expire)
    self._epoll.register(tfd.get_fd(), select.EPOLLIN | select.EPOLLET)
    _add_event(tfd, msg)
    tfd.set_time(v)
else:
    efd = EventFd()
    self._epoll.register(efd.get_fd(), select.EPOLLIN | select.EPOLLET)
    _add_event(efd, msg)
    efd.write()

参考
让事件飞 ——Linux eventfd 原理与实践 https://cloud.tencent.com/developer/article/1171508
Worker Pool With Eventfd https://www.yangyang.cloud/blog/2018/11/09/worker-pool-with-eventfd/
eventfd 函数使用介绍 https://blog.csdn.net/albertono1/article/details/73656962
https://linux.die.net/man/2/eventfd

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值