源码解读 TDengine 中线程池的实现
这篇文章中提到了 tsched 的源码可以一读,所以去阅读了一下,总共220来行。
1. 阅读前工作
通过上文了解到这段程序实现的是一个任务队列,同时带有线程池。这段程序是计算机操作系统里经典的consumer-producer (生产者-消费者)问题的实现。凡是学过操作系统这门课的,都应该知道这个问题,做过习题。在阅读源码之前可以先尝试用伪代码实现上述生产者-消费者问题。
2. 如何阅读?
了解清楚使用场景
这是一个线程池,客户端可以提交任务,线程池按照顺序调度执行任务。通过阅读 tsched.h 头文件,知道主要有三个函数:
- 初始化命名的调度器、线程池:taosInitScheduler
- 生产者提交某个任务:taosScheduleTask
- 程序结束时的清理工作:taosCleanUpScheduler
通过搜索上述三个函数的调用, 知道初始化了两个调度器,有三个地方会提交任务。
两个线程池
- 定时器里的 tmr 线程池 : 队列长度一万,只有一个线程服务。此线程会执行到期的 timer 的回调函数。
- tsc 线程池:队列长度一万,线程数量为所在机器 CPU 核心数的一半。这些线程负责:异步操作如执行语句,固定大小滑动窗口流式数据处理
两个生产者
上面提到了,有三个生产者会提交任务给线程池:
了解了清楚使用方、使用场景后,就容易读懂逻辑了。这里是一个标准的操作系统中生产者消费者的问题,用的也是标准解法:使用一个互斥量,两个信号量。线程池使用 pthread 来创建。
关键的数据结构
SSchedQueue 里面就是上述问题中的核心数据结构,除了放置上述提到的互斥量,信号量,还需要一个队列来存储要具体执行的任务。
SSchedMsg 结构来表示线程池任务,包含要执行的具体函数及所需参数。
源码里注释并不多,只能通过看具体实现来了解上述支持的执行模式。看到支持两种模式:执行fp,或者执行 tfp(ahandle, thandle)。
核心调度逻辑
上面提到了生产者,一直没有提到消费者。接着读 sched.c 里的源码,可以看到消费者就是线程池里每个线程的主框架逻辑: taosProcessSchedQueue。平常这些线程处于阻塞状态,等待任务。一旦生产者提交任务后,就会通知到消费者。消费者拿到提交的任务及参数,去执行。执行完之后继续进入上述阻塞的状态,这样周而复始。
这里有个疑问,消费者和生产者之间是异步的。消费完之后,总得有办法通知消费者,这一步在哪里做呢?读到这里可以花点时间翻翻源码,找找答案。
其实秘密也藏在当时提交任务的数据结构里。TDengine 里有样例代码,翻了翻,找到了这个 async demo。可以看到 taos_query_a 就是一个异步的query函数,里面带了 query语句异步执行完成后的回调函数:taos_insert_call_back)。
3. 一些思考
看的时候内心不断在思考、对比,比如优势、劣势是什么?我会怎么实现
优势
为何使用线程池?
- 通过固定线程池大小来固定资源开销,而且是程序初始化时申请资源,这在嵌入式设备里是非常重要的,如果资源不够用,那就快速失败,在程序一开始启动时就报错。
- 复用了线程,因为创建、销毁线程都是有开销的。这样在频繁创建、销毁线程情况下,可以节省开销,复用之前的线程。
- 任务和线程解耦:需要使用多线程的地方,只管提交任务就好了。线程的初始化、运行、状态切换由线程池来负责。
劣势
- 操作异步化,对程序员的心智要求更高。需要使用回调函数,需要存储上下文。但是在上述场景里还好, 都是一些固定的逻辑。
- 调试较麻烦,不是直来直去的逻辑。需要通过分析上下文及回调函数里的日志来分析问题。
有没有其他实现方式?
如果用 Go 语言实现,会很简单。使用 channel 来做任务分发,本身就是线程安全的。
使用 C 来写,个人觉得会限制 TDengine 的开源参与方。因为现在市场上会 C 的人比较少,而且主要集中在嵌入式领域。而且 C 的生态一般,语言的轮子比较少,所以很多工作都需要自己做,比如 http server,rpc 等。如果让我来设计实现 TDengine,我可能会优先考虑 Rust,既能精准控制内存,又有比较完善的社区,而且语言处于上升期,容易成为其中的明星项目,会有推广优势,比如能吸引一些本身对数据库不怎么关注,但是对 Rust 感兴趣的程序员。
4. 一个思考题
通过搜索 pthread_create 可以发现系统中还有其他创建线程的地方,并没有用到上述的线程池,比如 dnodeMWrite, TcpPool,cache,sync等。这些地方为什么没有使用线程池呢?
源码解读 TDengine 中线程池的实现的更多相关文章
- 硬核干货:4W字从源码上分析JUC线程池ThreadPoolExecutor的实现原理
前提 很早之前就打算看一次JUC线程池ThreadPoolExecutor的源码实现,由于近段时间比较忙,一直没有时间整理出源码分析的文章.之前在分析扩展线程池实现可回调的Future时候曾经提到并发 ...
- Java并发包源码学习系列:线程池ThreadPoolExecutor源码解析
目录 ThreadPoolExecutor概述 线程池解决的优点 线程池处理流程 创建线程池 重要常量及字段 线程池的五种状态及转换 ThreadPoolExecutor构造参数及参数意义 Work类 ...
- Java并发包源码学习系列:线程池ScheduledThreadPoolExecutor源码解析
目录 ScheduledThreadPoolExecutor概述 类图结构 ScheduledExecutorService ScheduledFutureTask FutureTask schedu ...
- JUC源码学习笔记5——线程池,FutureTask,Executor框架源码解析
JUC源码学习笔记5--线程池,FutureTask,Executor框架源码解析 源码基于JDK8 参考了美团技术博客 https://tech.meituan.com/2020/04/02/jav ...
- 《java.util.concurrent 包源码阅读》13 线程池系列之ThreadPoolExecutor 第三部分
这一部分来说说线程池如何进行状态控制,即线程池的开启和关闭. 先来说说线程池的开启,这部分来看ThreadPoolExecutor构造方法: public ThreadPoolExecutor(int ...
- 从源码角度来分析线程池-ThreadPoolExecutor实现原理
作为一名Java开发工程师,想必性能问题是不可避免的.通常,在遇到性能瓶颈时第一时间肯定会想到利用缓存来解决问题,然而缓存虽好用,但也并非万能,某些场景依然无法覆盖.比如:需要实时.多次调用第三方AP ...
- Mybatis源码解读-SpringBoot中配置加载和Mapper的生成
本文mybatis-spring-boot探讨在springboot工程中mybatis相关对象的注册与加载. 建议先了解mybatis在spring中的使用和springboot自动装载机制,再看此 ...
- 源码角度分析-newFixedThreadPool线程池导致的内存飙升问题
前言 使用无界队列的线程池会导致内存飙升吗?面试官经常会问这个问题,本文将基于源码,去分析newFixedThreadPool线程池导致的内存飙升问题,希望能加深大家的理解. (想自学习编程的小伙伴请 ...
- 《java.util.concurrent 包源码阅读》09 线程池系列之介绍篇
concurrent包中Executor接口的主要类的关系图如下: Executor接口非常单一,就是执行一个Runnable的命令. public interface Executor { void ...
随机推荐
- 使用 VMware Workstation Pro让 PC 提供云桌面服务——学习笔记(三)
目标 当在前面两篇博客后,我们已经创建了一个能当服务器的虚拟机,这时我们需要通过复制虚拟机来让创建更多虚拟机 操作步骤 1.创建克隆 这里主要是VMware软件的操作 虚拟机->管理->克 ...
- Unity3D制作类似吃鸡的小地图
先看效果图: 实现的效果就是右上角的一个小地图,会随着人物的移动而移动,显示人物的方向,并且可以展示地图设定范围的其他的玩家 制作起来也很简单,不需要任何代码.主要原理就是先创建Render Text ...
- CompletableFuture异步线程
1.线程池七大参数介绍 (1)corePoolSize:线程池中常驻核心线程数 (2)maximumPoolSize:线程池能够容纳同时执行的最大线程数,此值必须大于等于1 (3)keepAliveT ...
- Spring Security报异常 Encoded password does not look like BCrypt
控制台报错: Encoded password does not look like BCrypt 意思是前端传回去的密码格式与数据库里的密码格式不匹配,这样即使密码正确也无法校验.自然也就无法登录. ...
- 自建本地服务器,自建Web服务器——保姆级教程!
搭建本地服务器,Web服务器--保姆级教程! 本文首发于https://blog.chens.life/How-to-build-your-own-server.html. 先上图!大致思路就是如此. ...
- Jmeter 常用函数(26)- 详解 __chooseRandom
如果你想查看更多 Jmeter 常用函数可以在这篇文章找找哦 https://www.cnblogs.com/poloyy/p/13291704.html 作用 从指定的范围里面取值 语法格式 ${_ ...
- go thrift demo
接口:https://gowalker.org/github.com/apache/thrift/lib/go/thrift 参考文件:https://cong.im/2018/05/14/other ...
- 易盛信息9.0外盘期货行情数据API接口公共授权开发包例子代码
易盛信息9.0外盘期货行情数据API接口公共授权开发包例子代码 怎么才能获取到外盘期货行情数据API接口呢?不少朋友就会考虑到易盛9.0行情API接口,本身易盛就是一个软件提供商,提供行 ...
- Mysql安装错误
[问题一]在安装mysql时遇到以下错误 执行./mysqld --initialize 后 ./bin/mysqld: error while loading shared libraries: l ...
- PyTorch入门-CIFAR10图像分类
CIFAR10数据集下载 CIFAR10数据集包含10个类别,图像尺寸为 3×32×32 官方下载地址很慢,这里给一个百度云: https://pan.baidu.com/s/1oTvW8wNa-VO ...