brpc源码解析(四)—— Bthread机制
目录
- 一、概述
- 二、启动入口函数
- 三、内部启动函数
- 四、worker工作入口
- 五、总结
Bthread是brpc用到的一个线程库,也是brpc的核心之一,默认情况下,包括用户代码在内的绝大部分代码都是运行在bthread里的,bthread也是brpc实现高性能的基石。
一、概述
bthread在官方文档里定义如下:
bthread是baidu-rpc使用的M:N线程库,目的是在提高程序的并发度的同时,降低编码难度,并在核数日益增多的CPU上提供更好的scalability, cache locality。”M:N“是指M个bthread会映射至N个pthread,一般M远大于N。由于linux当下的pthread实现(NPTL)是1:1的,M个bthread也相当于映射至N个LWP。
Goals
1.用户可以延续同步的编程模式,能很快地建立bthread,可以用多种原语同步。
2.bthread所有接口可以在pthread中被调用并有合理的行为,使用bthread的代码可以在pthread中正常执行。 能充分利用多核。
3.更好的cache locality,更低的延时。
NonGoals
1.提供pthread的兼容接口,只需链接即可使用。拒绝理由:bthread没有优先级,不适用于所有的场景,链接的方式容易使用户在不知情的情况下误用bthread,造成bug。
2.修改内核让pthread支持同核快速切换。拒绝理由:拥有大量pthread后,每个线程对资源的需求被稀释了,基于thread-local cache的代码效果都会很差,比如tcmalloc。而独立的bthread不会有这个问题,因为它最终还是被映射到了少量的pthread。
官方地定义很好地总结了bthread的设计理念,个人认为核心就是用户可以延续同步的编程模式,但获得优秀的并发,并且保持了作为用户态线程的合理的克制,虽然能在pthread运行但对pthread没有侵入性。关于bthread的官方文档非常详细,想要深入了解可以参考官方文档,这里着重介绍一下在实际使用过程中bthread的一些运行机制。
之所以要采用这么一种M:N的机制,是为了兼顾当前多核cpu以及调度竞争上,考虑两种极端情况,一是每个用户线程对应一个内核线程,如果是这种模型,对多核cpu的利用会很充分,但是调度成本(用户态内核态的切换)以及线程间的数据同步成本都比较高,而是所有的用户线程都在一个内核线程的情况,这种情况下调度成本和数据同步成本低,但很难利用多核cpu的能力,同时用户线程也容易block。所以M:N线程库bthread的主要思想就是,M 个bthread 可以运行在N个内核线程上,也就是有N个worker分别运行在N个pthread上,所有的bthread都是在worker上调度运行,woker运行完一个bthread后就会去队列里调度下一个bthread,既可以从当前worker的本地队列里调度,也可以从其他worker的队列里取,也就是所谓的work stealing机制,该机制也是bthread核心设计之一。
二、启动入口函数
作为一个线程库,用户最常用的自然是启动线程,bthread的启动入口函数有两个:
(1)bthread_start_urgent:让出当前worker立即执行新bthread。
(2)bthread_start_background:将要启动的bthread放入队列等待调度。
函数名也说明了各自的行为。函数声明如下:
函数定义如下:
可以看到,二者很类似,都是先去获取了一个TaskGroup指针类型的变量g,然后根据g是否为null执行不同的逻辑。这里就涉及到bthread的taskgroup的概念,taskgroup的字面意思是任务组,一个taskgroup对应一个worker,所有的taskgroup由一个task_control的单例控制。在task_group.cpp里,有一个__thread变量tls_task_group,该变量在bthread.cpp里和task_control.cpp里均通过extern方式共用,也就是有一个thread-local的全局变量bthread::tls_task_group:
这个tls变量很重要,表明当前线程所归属的taskgroup,如果为null,说明当前线程不是bthread。
在上述启动bthread的函数里,首先就会判断taskgroup是否为null,如果不为null,则表明当前就是bthread而且taskgroup指明了对应的taskgroup,在对应taskgroup(worker)执行新bthread的启动即可,区别就是bthread_start_urgent调用内部的start_foreground,而bthread_start_background调用内部的start_background,否则属于在非worker上新启bthread,这个场景两个入口函数效果一样。
三、内部启动函数
bthread_start_urgent调用的TaskGroup::start_foreground函数核心部分如下:
这个函数里主要就是为要执行的任务新建taskmeta并对各种变量赋值,最重要的部分是fn和arg的赋值,然后最终直接调度执行。
而start_background和start_foreground最大的区别在于不是直接调度执行,如下:
REMOTE是函数模板参数,表明是否是从worker启动,为false说明不是远程,也就是是从worker启动。上面bthread_start_background直接调用start_foreground用的参数就是false,因为是从worker启动的。这个参数决定了调用ready_to_run_romote还是ready_to_run,二者分别如下:
二者的主要区别是对于来自非worker的bthread,放入_remote_rq队列,来自worker的放入_rq队列,总的来说是一个把任务加入队列然后调用signal按需唤醒woker的过程。调度顺序上,本地队列_rq的优先级比_remote_rq高,而且_rq是wait-free的,_remote_rq是用mutex保护的。
入口函数bthread_start_urgent或者bthread_start_background如果发现taskgroup是为null,则说明调用方不是在bthread里,这个时候去调用的start_from_non_worker函数如下:
该函数里会首先检查是否已经有task_control单例了,如果有说明其他地方已经建立过bthread了,也就已经启动了一定数量的taskgroup,如果没有说明是首次启动bthread,需要创建,new taskcontrol后会执行taskcontrol的init,核心就是用pthread启动指定数量的worker,如下:
得到taskcontrol单例后,用taskcontrol选取一个taskgroup,新建bthread进行调度。
上图里worker_thread函数如下:
先是调用create_group新建并初始化task_group,随后调用run_main_task,也就是worker的工作入口
四、worker工作入口
run_main_task的核心就是不断循环等待可以执行的bthread,包括去其他的woker steal,然后执行。如下:
一旦拿到了可以执行的任务,则调用sched_to进行执行,里面是一些context的切换后执行之类的底层操作。前面提到了,如果在一个bthread里执行bthread_start_urgent,会调用TaskGroup::start_foreground,里面也是调用TaskGroup::sched_to立即让出当前worker。
上面调用的TaskGroup::sched_to函数如下:
根据传入的next_tid取出对应bthread的meta信息,如果对应meta的stack为空,说明这是一个新建的bthread,则会调用get_stack从一个object pool类型的资源池里取出stack对象赋给bthread,object pool继承自resource pool。get_stack函数如下:
注意get_stack所用的参数:get_stack(next_meta->stack_type(), task_runner),第一个参数是stack类型,主要是按大小分为了好几种,第二个task_runner则是真正的用户函数的入口函数,task_runner内部核心代码如下:
m->fn(m-arg)就是使用启动bthread时提供的用户函数和对应参数进行调用。这个task_runner经过层层调用最终会作为一个叫做bthread_make_fcontext的汇编实现的函数的参数,该函数真正构造一个从taskrunner开始执行的stack,如下:
确保stack就绪后再调用内部的另一个void TaskGroup::sched_to(TaskGroup** pg, TaskMeta* next_meta)重载去执行切换操作。这个函数里比较长,核心部分如下:
切换threadlocal变量
进行堆栈的切换,也是bthread切换最关键的部分,jump_stack函数如下:
调用的是bthread_jump_fcontext,和bthread_jump_fcontext一样也是由汇编实现的,真正的去操作寄存器等完成线程切换。
五、总结
这篇文章介绍了bthread主要的启动和调度过程。除了这些,bthread还实现了butex,提供了类似pthread的同步原语,同时也有和pthread类似的bthread_join接口和tls支持接口等,总的来说接口和pthread很类似,同时因为bthread是跑在pthread里的,也支持在bthread里调用阻塞的pthread接口直接阻塞worker,具体的可以参照官方文档和注释很清晰的源码,这里不再赘述。
brpc源码解析(四)—— Bthread机制相关推荐
- Redis源码解析(15) 哨兵机制[2] 信息同步与TILT模式
Redis源码解析(1) 动态字符串与链表 Redis源码解析(2) 字典与迭代器 Redis源码解析(3) 跳跃表 Redis源码解析(4) 整数集合 Redis源码解析(5) 压缩列表 Redis ...
- brpc源码解析(二)—— brpc收到请求的处理过程
文章目录 一.基本设计思路 二.实现细节 三.总结 作为rpc服务器,在启动过后,最主要的一个过程就是收到请求后的处理,而这就牵涉到一个网络编程相关最基本的部分:如何有效地处理socket传过来地数据 ...
- loraserver 源码解析 (四) lora-gateway-bridge
lora-gateway-bridge 负责接收 gateway 通过 udp 发送的 packet-forwarder 数据 然后通过 MQTT broker 将报文转发给 LoRa Server ...
- brpc源码解析(七)—— worker基于ParkingLot的bthread调度
前面已经介绍过了bthread的主要机制,但对于具体的调度细节没有过多涉及,本篇将在着重介绍下bhtread在各个worker(taskgroup)之间的调度方式. 在brpc里,有个和调度相关的重要 ...
- brpc源码解析(一)—— rpc服务添加以及服务器启动主要过程
目录 1.往Server里添加Service(业务代码) 2.设置服务器参数 3.启动服务器 平时的工作用到了baidu-rpc搭建rpc服务,作为戈君大神的大作,在没有开源的时候,这个c++ 的rp ...
- 聊聊 Kafka: Consumer 源码解析之 Rebalance 机制
一.前言 我们上一篇分析了 Consumer 如何加入 Consumer Group,其实上一篇是一个很宏观的东西,主要讲 ConsumerCoordinator 怎么与 GroupCoordinat ...
- Android源码解析触碰机制
分发 dispatchTouchEvent 触碰屏幕时会触发的view方法,原理需要看更深层次的源码,这里可以理解为入口 拦截 onInterceptTouchEvent 消费 onTouch ...
- Tomcat源码解析四:Tomcat关闭过程
我们在Tomcat启动过程(Tomcat源代码阅读系列之三)一文中已经知道Tomcat启动以后,会启动6条线程,他们分别如下: [java] view plaincopy "ajp-bio- ...
- arcengine遍历属性表_Redis源码解析四--跳跃表
Redis 跳跃表(skiplist) 1. 跳跃表(skiplist)介绍 定义:跳跃表是一个有序链表,其中每个节点包含不定数量的链接,节点中的第i个链接构成的单向链表跳过含有少于i个链接的节点. ...
最新文章
- 智能音箱玩出新花样?这家公司推出2699元的智能虚拟机器人
- r 字符串转化为数值_Lua 字符串处理
- 神经网络模拟条件反射
- 【Python教程】sort 与sorted的用法与区别
- php海外研发,国外主流PHP框架比较
- 实时检索系统Zoie实现分析
- linux 下小技巧之-统计文件夹下面子文件夹下面的个数
- 心理学家发现脚部动作可表现性格特征
- 在线教育开源 java_新款SpringBoot在线教育平台开源了
- iOS开发:报错The sandbox is not in sync with the Podfile.lock. Run ‘pod install‘ …的解决方法
- Geos库在Windows上的编译
- uni-app创建并运行微信小程序项目
- 计算机的工作原理(冯诺依曼体系)
- OSG智能指针---Referenced类
- [极致用户体验] 微信设置大字号后,iOS加载网页时闪动怎么办?
- Android Hierarchy Viewer
- Java开发翻译系统
- centos5部署open***
- 【Java】函数式编程学习笔记——Stream流
- erp系统 服务器在哪里的,云erp服务器在哪
热门文章
- 未来混合云的发展可能有两个方向 | 5G技术研发试验第三阶段规范正式发布
- NM_CUSTOMDRAW消息
- Typora1.0.2 + SMMS上传图片
- Docker笔记 —— 简介与安装 + Centos的一些配置
- VB编程:UCase转大写,LCase转小写-4_彭世瑜_新浪博客
- python爬取去哪网数据_用户观点:企查查数据爬取技术与Python 爬取企查查数据...
- 青柠开车Spring Cloud(三) —— Spring cloud Eureka
- Spring 源码--Bean 实例化
- python(3.10,Win10 64位)的wordcloud安装
- pc端页面右侧滑动样式修改