Muduo网络库源码分析(三)线程间使用eventfd通信和EventLoop::runInLoop系列函数
来源:互联网 发布:知乎为什么封号 编辑:程序博客网 时间:2024/06/15 04:04
先说第一点,线程(进程)间通信有很多种方式(pipe,socketpair),为什么这里选择eventfd?
eventfd 是一个比 pipe 更高效的线程间事件通知机制,一方面它比 pipe 少用一个 file descripor,节省了资源;另一方面,eventfd 的缓冲区管理也简单得多,全部“buffer” 只有定长8 bytes,不像 pipe 那样可能有不定长的真正 buffer。
最重要的一点:当我们想要编写并发型服务器的时候,eventfd 可以完美取代 pipe去通知(唤醒)其他的进程(线程)。比如经典的异步IO reactor/selector 应用场景,去唤醒select的调用。可以和事件通知机制完美的的结合。
(一)eventfd
#include <sys/eventfd.h>int eventfd(unsigned int initval, intflags);
简单的应用示例:
#include <sys/eventfd.h> #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <stdint.h> /* Definition of uint64_t */ #define handle_error(msg) \ do { perror(msg); exit(EXIT_FAILURE); } while (0) int main(int argc, char *argv[]) { uint64_t u; int efd = eventfd(10, 0); if (efd == -1) handle_error("eventfd"); int ret = fork(); if(ret == 0) { for (int j = 1; j < argc; j++) { printf("Child writing %s to efd\n", argv[j]); u = atoll(argv[j]); ssize_t s = write(efd, &u, sizeof(uint64_t)); if (s != sizeof(uint64_t)) handle_error("write"); } printf("Child completed write loop\n"); exit(EXIT_SUCCESS); } else { sleep(2); ssize_t s = read(efd, &u, sizeof(uint64_t)); if (s != sizeof(uint64_t)) handle_error("read"); printf("Parent read %llu from efd\n",(unsigned long long)u); exit(EXIT_SUCCESS); } }
(二)EventLoop::loop、runInLoop、queueInLoop、doPendingFunctors
先看一下这四个函数总体的流程图:
依次解释:
// 该函数可以跨线程调用 void EventLoop::quit() { quit_ = true; if (!isInLoopThread()) { wakeup(); } } //使用eventfd唤醒 void EventLoop::wakeup() { uint64_t one = 1; //ssize_t n = sockets::write(wakeupFd_, &one, sizeof one); ssize_t n = ::write(wakeupFd_, &one, sizeof one); if (n != sizeof one) { LOG_ERROR << "EventLoop::wakeup() writes " << n << " bytes instead of 8"; } }
view plain co
如果不是当前IO线程调用quit,则需要唤醒(wakeup())当前IO线程,因为它可能还阻塞在poll的位置(EventLoop::loop()),这样再次循环判断 while (!quit_) 才能退出循环。view pl
关于doPendingFunctors 的补充说明:
1、不是简单地在临界区内依次调用Functor,而是把回调列表swap到functors中,这样一方面减小了临界区的长度(意味着不会阻塞其它线程的queueInLoop()),另一方面,也避免了死锁(因为Functor可能再次调用queueInLoop())
2、由于doPendingFunctors()调用的Functor可能再次调用queueInLoop(cb),这时,queueInLoop()就必须wakeup(),否则新增的cb可能就不能及时调用了
3、muduo没有反复执行doPendingFunctors()直到pendingFunctors_为空而是每次poll 返回就执行一次,这是有意的,否则IO线程可能陷入死循环,无法处理IO事件。
总结一下就是:
假设我们有这样的调用:loop->runInLoop(run),说明想让IO线程执行一定的计算任务,此时若是在当前的IO线程,就马上执行run();如果是其他线程调用的,那么就执行queueInLoop(run),将run异步添加到队列,当loop内处理完事件后,就执行doPendingFunctors(),也就执行到了run();最后想要结束线程的话,执行quit。
参考:
《linux多线程服务端编程》
http://blog.csdn.net/yusiguyuan/article/details/40593721?utm_source=tuicool&utm_medium=referral
阅读全文
0 0
- Muduo网络库源码分析(三)线程间使用eventfd通信和EventLoop::runInLoop系列函数
- Muduo网络库源码分析(三)线程间使用eventfd通信和EventLoop::runInLoop系列函数
- muduo源码分析之EventLoop::runInLoop()函数
- muduo网络库学习之EventLoop(二):进程(线程)wait/notify 和 EventLoop::runInLoop
- muduo网络库学习笔记(11):有用的EventLoop::runInLoop()函数
- Muduo网络库源码分析(一) EventLoop事件循环(Poller和Channel)
- Muduo网络库源码分析(一) EventLoop事件循环(Poller和Channel)
- muduo网络库:线程之间的同步机制(使用eventfd函数,条件变量,线程池)
- linux-muduo线程通信eventfd
- muduo源码分析---EventLoop类
- muduo源码分析之EventLoop
- muduo源码分析--EventLoop 类的实现
- 线程间使用wait和notify通信
- muduo网络库学习之EventLoop(一):事件循环类图简介和muduo 定时器TimeQueue
- muduo网络库学习之EventLoop(七):TcpClient、Connector
- muduo网络库学习(四)事件驱动循环EventLoop
- [Muduo网络库源码分析] (10) base/ThreadPoll_cc_h_线程池
- muduo::EventLoop分析
- 腾讯云互动直播分享屏幕小结
- php-fpm nginx配置文件 处理 Permission denied 错误
- 图像基本变换---图像二值化(包含OSTU/迭代法/统计法/双峰法/P分位法/最大熵法)
- Maximum Subarray
- 使用 textview实现文字的阴影效果,浮雕效果
- Muduo网络库源码分析(三)线程间使用eventfd通信和EventLoop::runInLoop系列函数
- Let's Encrypt证书使用
- ORA-01427: 单行子查询返回多个行
- Swagger2 (1)到此一游集成spring boot
- redhat EL6.5下的mysql5.7并行复制原理及基本配置
- 平滑,3D,iTunes风格的 图像列表控件CListCtrl-CAlbumCtrl类
- c++集成Python
- ubuntu16.04 latex应用
- Linux信号处理机制及处理函数