libhv之hloop源码分析

news2024/11/29 2:46:20
1int hloop_run(hloop_t* loop)

hloop_run总结
1.loop状态判断和管理
2.loop的flags管理(HLOOP_FLAG_QUIT_WHEN_NO_ACTIVE_EVENTS,HLOOP_FLAG_RUN_ONCE,HLOOP_FLAG_AUTO_FREE
3.创建custom event通信fd,方便loop进行custom event事件传递
4.hloop_process_events真正处理事件的函数

int hloop_run(hloop_t* loop) {
	//状态控制已经状态判断,loop基于单线程运行模式,所以状态无锁
    if (loop == NULL) return -1;
    if (loop->status == HLOOP_STATUS_RUNNING) return -2;

    loop->status = HLOOP_STATUS_RUNNING;
    loop->pid = hv_getpid();
    loop->tid = hv_gettid();
    
	//用户custom event通信,即pairsocket
    if (loop->intern_nevents == 0) {
        hmutex_lock(&loop->custom_events_mutex);
        if (loop->eventfds[EVENTFDS_WRITE_INDEX] == -1) {
            hloop_create_eventfds(loop);
        }
        hmutex_unlock(&loop->custom_events_mutex);

#ifdef DEBUG
        htimer_add(loop, hloop_stat_timer_cb, HLOOP_STAT_TIMEOUT, INFINITE);
        ++loop->intern_nevents;
#endif
    }

    while (loop->status != HLOOP_STATUS_STOP) {
        if (loop->status == HLOOP_STATUS_PAUSE) {
            hv_msleep(HLOOP_PAUSE_TIME);
            hloop_update_time(loop);
            continue;
        }
        ++loop->loop_cnt;
        
        //hloop标志,参考epool的flag
        //当没有激活事件的时候,退出loop
        if ((loop->flags & HLOOP_FLAG_QUIT_WHEN_NO_ACTIVE_EVENTS) &&
            loop->nactives <= loop->intern_nevents) {
            break;
        }
		
		//真正处理事件的函数
        hloop_process_events(loop, HLOOP_MAX_BLOCK_TIME);
		
		//只运行一次,然后退出loop
        if (loop->flags & HLOOP_FLAG_RUN_ONCE) {
            break;
        }
    }

    loop->status = HLOOP_STATUS_STOP;
    loop->end_hrtime = gethrtime_us();
	
	//自动释放loop
    if (loop->flags & HLOOP_FLAG_AUTO_FREE) {
        hloop_cleanup(loop);
        HV_FREE(loop);
    }
    return 0;
}
2.int hloop_process_events(hloop_t* loop, int timeout_ms)

hloop_process_events总结

  1. 处理事件优先级:timer > io > idle
  2. 如果有timer事件并且timeout,则加入pendding队列,并且立马处理timer事件,跳过io事件
  3. 如果存在io并且没有触发timer的情况下,则检查io状态,如果io事件触发则加入pendding队列
  4. custom 事件不进入pendding队列,而是直接进入eventfd_read_cb
  5. 当不存在pendding事件,则触发idle
  6. 真正处理事件的是hloop_process_pendings
int hloop_process_events(hloop_t* loop, int timeout_ms) {
    // ios -> timers -> idles
    int nios, ntimers, nidles;
    nios = ntimers = nidles = 0;

    // calc blocktime
    int32_t blocktime_ms = timeout_ms;
    //如果存在定时器,则检查定时器是否超时,
    //由于这里是基于小根堆实现的定时器,所以只需要检查根节点是否超时即可
    if (loop->ntimers) {
        hloop_update_time(loop);
        int64_t blocktime_us = blocktime_ms * 1000;
        if (loop->timers.root) {
            int64_t min_timeout = TIMER_ENTRY(loop->timers.root)->next_timeout - loop->cur_hrtime;
            blocktime_us = MIN(blocktime_us, min_timeout);
        }
        if (loop->realtimers.root) {
            int64_t min_timeout = TIMER_ENTRY(loop->realtimers.root)->next_timeout - hloop_now_us(loop);
            blocktime_us = MIN(blocktime_us, min_timeout);
        }
		//如果已经超时,则说明定时器已经触发,直接处理定时器
        if (blocktime_us < 0) goto process_timers;
        blocktime_ms = blocktime_us / 1000 + 1;
        blocktime_ms = MIN(blocktime_ms, timeout_ms);
    }
	
	//如果存在io则处理iowatcher_poll_events,不同的模型,实现不一样
	//如果io事件被触发则加入pendding队列
    if (loop->nios) {
        nios = hloop_process_ios(loop, blocktime_ms);
    } else {
        hv_msleep(blocktime_ms);
    }
    hloop_update_time(loop);
    // wakeup by hloop_stop
    if (loop->status == HLOOP_STATUS_STOP) {
        return 0;
    }

process_timers:
	//处理定时器,并加入pendding队列,真正触发cb的是hloop_process_pendings
    if (loop->ntimers) {
        ntimers = hloop_process_timers(loop);
    }
	
	//如果不存pending事件,则触发idles事件
    int npendings = loop->npendings;
    if (npendings == 0) {
        if (loop->nidles) {
            nidles= hloop_process_idles(loop);
        }
    }
	//处理pendding事件
    int ncbs = hloop_process_pendings(loop);
    // printd("blocktime=%d nios=%d/%u ntimers=%d/%u nidles=%d/%u nactives=%d npendings=%d ncbs=%d\n",
    //         blocktime, nios, loop->nios, ntimers, loop->ntimers, nidles, loop->nidles,
    //         loop->nactives, npendings, ncbs);
    return ncbs;
}
3.int hloop_process_pendings(hloop_t* loop)

hloop_process_pendings总结

  1. 根据优先级遍历队列处理加入的pendding事件,如果存在回调则触发回调
int hloop_process_pendings(hloop_t* loop) {
    if (loop->npendings == 0) return 0;

    hevent_t* cur = NULL;
    hevent_t* next = NULL;
    int ncbs = 0;
    // NOTE: invoke event callback from high to low sorted by priority.
    //从高优先级遍历到低优先级,不同优先级的事件放在不同list
    for (int i = HEVENT_PRIORITY_SIZE-1; i >= 0; --i) {
        cur = loop->pendings[i];
        //遍历当前优先级下的所有事件,并且触发回调
        while (cur) {
            next = cur->pending_next;
            if (cur->pending) {
                if (cur->active && cur->cb) {
                    cur->cb(cur);
                    ++ncbs;
                }
                cur->pending = 0;
                // NOTE: Now we can safely delete event marked as destroy.
                if (cur->destroy) {
                    EVENT_DEL(cur);
                }
            }
            cur = next;
        }
        loop->pendings[i] = NULL;
    }
    loop->npendings = 0;
    return ncbs;
}

至此hloop_run的链路分析完毕。

总结

上述代码分析过程中,发现timer,io均是无锁模式(custom event除外)。得益于loop要求一个loop只能运行在一个线程中,避免了多线程竞争的问题。hloop_run基于单线程模式禁止跨线程使用loop,将触发事件加入pendding队列,最后由pendding队列统一处理cb。
在这里插入图片描述

问题:如果loop基于单线程模式,那么如果作为服务端是怎么实现高并发的呢?

答:参考libhv的 examples/multi-thread 目录下给出了几种常见的多线程/多进程模式的具体写法:

  • multi-acceptor-processes:多accept进程模式
  • multi-acceptor-threads:多accept线程模式
  • one-acceptor-multi-workers:一个accept线程+多worker线程

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/793076.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Docker consul容器服务更新与发现

Docker consul容器服务更新与发现 一、什么事服务注册与发现二、什么是consul三、consul部署1、consul服务器2、registrator服务器3、consul-template 一、什么事服务注册与发现 服务注册与发现是微服务架构中不可或缺的重要组件。起初服务都是单节点的&#xff0c;不保障高可…

MyBatis学习笔记之高级映射及延迟加载

文章目录 环境搭建&#xff0c;数据配置多对一的映射的思路逻辑级联属性映射association分布查询 一对多的映射的思路逻辑collection分布 环境搭建&#xff0c;数据配置 t_class表 t_stu表 多对一的映射的思路逻辑 多对一&#xff1a;多个学生对应一个班级 多的一方是st…

STM32 中断优先级管理(一)

STM32 NVIC 中断优先级管理 CM3内核支持256个中断&#xff0c;其中包含了16个内核中断和240个外部中断&#xff0c;并且有256级的可编程中断设置。 但STM32并没有使用CM3内核的全部东西&#xff0c;只用了一部分。 STM32有84个中断&#xff0c;包括16个内核中断和68个可屏蔽…

Windows数据类型LPSTR学习

Windows在C语言的基础之上又定义了一些Windows下的数据类型&#xff1b;下面学习一下LPSTR&#xff1b; LPSTR和LPWSTR是Win32和VC所使用的一种字符串数据类型。LPSTR被定义成是一个指向以NULL(‘\0’)结尾的32位ANSI字符数组指针&#xff0c;而LPWSTR是一个指向以NULL结尾的64…

Fiddler使用教程|渗透测试工具使用方法Fiddler

提示&#xff1a;如有问题可联系我&#xff0c;24小时在线 文章目录 前言一、Fiddler界面介绍二、菜单栏1.菜单Fiddler工具栏介绍Fiddler命令行工具详解 前言 网络渗透测试工具&#xff1a; Fiddler是目前最常用的http抓包工具之一。 Fiddler是功能非常强大&#xff0c;是web…

探秘MySQL底层架构:设计与实现流程

前言 Mysql&#xff0c;作为一款优秀而广泛使用的数据库管理系统&#xff0c;对于众多Java工程师来说&#xff0c;几乎是日常开发中必不可少的一环。无论是存储海量数据&#xff0c;还是高效地检索和管理数据&#xff0c;Mysql都扮演着重要的角色。然而&#xff0c;除了使用My…

全网最牛,Jmeter接口自动化-读取用例执行并结果回写(详细整理)

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 1、环境准备 下载…

(vue)vue项目获取日期 时间 星期

(vue)vue项目获取日期 时间 星期 效果&#xff1a; 代码&#xff1a; <div class"top-time"><div class"time-currentDate">{{ currentDate }}<div class"time-img"><img src"/assets/image/icon-time.png" a…

曲面显示器和平面显示器有什么区别?

电脑显示器的选择非常重要。除了现在平面显示器和曲面显示器也很流行之外&#xff0c;曲面显示器和平面显示器有什么区别呢&#xff1f;我们来看看曲面显示器和平面显示器的优缺点 如今&#xff0c;曲面屏显示器已成为主流。为电脑配置一个好的显示器非常重要。市场上有许多计算…

记录联想拯救者R720重装系统

文章目录 bios里找不到U盘启动项2023.7.23重装系统后数据记录C盘内存修改默认AppData的路径&#xff08;亲测&#xff0c;没用&#xff09; bios里找不到U盘启动项 制作好启动盘后&#xff0c;开机按F2进入bios后&#xff0c;找不到U盘启动项&#xff0c;如下图所示&#xff1…

常见面试题之设计模式--责任链模式

1. 概述 在现实生活中&#xff0c;常常会出现这样的事例&#xff1a;一个请求有多个对象可以处理&#xff0c;但每个对象的处理条件或权限不同。例如&#xff0c;公司员工请假&#xff0c;可批假的领导有部门负责人、副总经理、总经理等&#xff0c;但每个领导能批准的天数不同…

(原创)自定义DialogFragment以及解决其内存泄漏问题

前言 日常开发中&#xff0c;dialog是常见的功能&#xff0c;我们时常需要弹出来一些弹框提示用户 今天就定义了一个方便的dialog基类BaseSimpleDialogFragment&#xff0c; 支持快速地显示一个dialog 主要功能有&#xff1a; initAnimation&#xff1a;设置入场和出场动画 ge…

【青书学堂】管理学基础(直播课) 第一学期 考试

【青书学堂】计算机组装与维护(直播课) 第一学期 考试 标题最终成绩:83.34 分 注意:答案仅供参考 第1题 单选题 梅奥的霍桑试验表明( )。 A:非正式组织对组织目标的达成是有害的 B:非正式组织对组织目标的达成是有益的 C:企业应采取一切措施来取缔非正式组织 D:企业应该…

【Unity3D日常开发】Unity3D中比较string字符串的常用方法

推荐阅读 CSDN主页GitHub开源地址Unity3D插件分享简书地址我的个人博客 大家好&#xff0c;我是佛系工程师☆恬静的小魔龙☆&#xff0c;不定时更新Unity开发技巧&#xff0c;觉得有用记得一键三连哦。 一、前言 字符串string的比较有很多方法&#xff0c;比如&#xff1a; …

【动态规划】简单多状态

文章目录 动态规划&#xff08;简单多状态&#xff09;1. 按摩师2. 打家劫舍 ||3. 删除并获得点数4. 粉刷房子5. 最佳买卖股票时机含冷冻期6. 买卖股票的最佳时机含手续费7. 买卖股票的最佳时机 |||8. 买卖股票的最佳时机 IV 动态规划&#xff08;简单多状态&#xff09; 1. 按…

dp算法篇Day12

“我悲喜都&#xff0c;只换来这一场无声的野火。” 56、完全平方数 (1) 题目解析 ​​​​​​ 把题目解释到了这个份上&#xff0c;你很难不把思路转移到考虑 "背包问题上"。 (2) 算法原理 class Solution { public:int numSquares(int n) {int m sqrt(n);vec…

【Python基础】VS2019中使用Python及安装Python包

【Python基础】VS2019中使用Python及安装Python包 文章目录 前言一、VS2019中安装Python环境二、Python环境变量配置三、安装Python包总结 前言 要使用Python语言来写一些程序&#xff0c;使用哪个IDE是个问题&#xff0c;若是专业开发Python&#xff0c;PyCharm无疑是最佳选择…

RTI无线电层析成像Matlab仿真数据生成

文章目录 概述初始化环境参数逆面积椭圆模型 概述 无线电层析成像是一种通过获取一定区域内多对相对固定的无线通信节点间的某种测量数据后,按照一定的数学处理方法,对区域内的障碍物目标以图像的形式 展现出来的成像技术。 开山之作&#xff1a; J. Wilson and N. Patwari, …

【小白必看】Python爬虫实战:获取阴阳师网站图片并自动保存

文章目录 前言导入模块伪装自己发送请求获取地址列表获取所有背景的地址创建文件夹保存图片文件完整代码运行效果部分图片展示结束语 前言 本文介绍了一个使用Python编写的程序&#xff0c;用于获取指定网页的背景图片并保存到本地。在程序中使用了requests模块发送HTTP请求&a…

FCPX插件-15组金色华丽粒子特效闪耀动画 Awards Backgrounds

Awards Backgrounds是fcpx上一个很棒的电影级效果插件&#xff0c;Awards Backgrounds 包含15组金色华丽粒子特效闪耀动画&#xff0c;可以为您的作品创建豪华的背景或叠加特效&#xff01;包含各种带有可编辑颜色的下落闪闪发光粒子的场景。用于展示奖项提名者、优雅的表演、祝…