- 博客(45)
- 收藏
- 关注
原创 C++ 无锁队列实现:从 SPSC 到 MPSC
本文围绕一个有界环形队列展开,先用 SPSC 版本说明最基本的生产者消费者模型,再重点分析 MPSC 版本的迭代过程。MPSC 的核心难点不在“多个线程怎么拿到不同位置”,而在“多个生产者完成构造的顺序可能和认领位置的顺序不同”,因此不能把tail_当作可读边界,也不能把commit_简单理解成完成数量,最终必须使用表示连续可读前缀和每个槽位对应的精确票号。
2026-05-17 15:58:08
434
原创 互斥锁、自旋锁、读写锁使用场景以及底层实现
在多线程程序中,锁的本质是协调多个线程对共享资源的访问,但不同锁的设计目标并不一样。互斥锁更关注“抢不到锁时不要浪费 CPU”,自旋锁更关注“短时间等待时不要陷入内核调度”,读写锁更关注“读多写少时让多个读线程并发执行”从底层实现看,互斥锁通常是的组合,自旋锁通常是的组合,读写锁通常是的组合。下面从使用场景、底层结构、加锁解锁流程三个角度分别分析这三类锁。
2026-05-16 19:37:17
363
原创 C++ 原子变量与内存序:从std::atomic到release/acquire
如果一个原子变量只是用来做计数、打点、统计次数,并且其他线程不依赖这个计数去判断某段数据是否已经准备好,那么 relaxed 通常就够了。例如可以保证计数本身不会丢失更新,但不额外约束其他内存读写的顺序。
2026-05-16 16:09:49
457
原创 Redis 跳表实现详解
跳表可以理解为“带多级索引的有序链表”,底层保存全部数据,上层保存稀疏索引,查找时从高层快速跳跃,最终落到底层精确定位Redis zset 选择跳表,是因为它既要支持有序范围查询,又要支持频繁插入删除,跳表用随机层数避免了复杂的树旋转和节点分裂,在工程实现上更直接我的保留了跳表最核心的机制,负责生成层数,update数组负责记录每一层前驱,插入和删除本质上都是多层链表指针修改。
2026-05-16 13:36:24
422
原创 Redis 渐进式 rehash:为什么要分批搬迁哈希表
Redis 渐进式 rehash 解决的是哈希表扩容、缩容时一次性搬迁成本过高的问题Redis 字典通过ht[0]和ht[1]两张表配合rehashidx实现迁移状态管理,正常情况下只使用ht[0],扩容或缩容时创建ht[1],然后分批把旧表数据搬到新表在 rehash 期间,查询、修改、删除需要兼容两张表,新增数据直接写入新表,保证旧表元素只减不增,迁移过程会分摊到后续命令和定时任务中执行。
2026-05-16 12:25:05
450
原创 Kafka 架构讲解:从提交日志到分区副本机制
Kafka 写消息时,不是把消息随机插入到某个位置,也不是消费完就立即删除,而是将消息追加到某个 Partition 的日志末尾。每条消息被追加成功后,就会得到一个 offset这和传统队列最大的区别是:传统队列更强调消息被取走后就消失,而 Kafka 更强调消息被持久化保存,消费者只是移动自己的消费进度。
2026-05-15 13:40:52
570
2
原创 gRPC Keepalive 机制
gRPC 的 Keepalive 基于,不是 TCP keepalive它主要解决两个问题1、检测死连接客户端和服务端之间的 TCP 连接看起来还在,但对端进程可能已经崩溃,网络可能已经断开,中间 NAT 也可能已经丢了连接状态,这时如果没有探测,业务代码可能要等到下一次 RPC 请求失败才知道连接不可用,而 Keepalive 会主动发送 HTTP/2 PING,如果超过还没有收到 ACK,就认为连接已经不可用,然后关闭连接并重新建立连接2、保持连接活跃。
2026-05-14 20:20:17
426
原创 gRPC 数据包传输格式解析:从 Protobuf 到 HTTP/2
假设有这样一个protoid: 150我们重点分析这个对象在网络中到底会变成什么样。
2026-05-14 14:27:15
587
原创 RingBuffer:面向网络编程的环形缓冲区实现
RingBuffer 也叫环形缓冲区,本质上是一段固定大小的连续内存它的特点是,写到末尾后可以从头继续写,逻辑上看起来像一个环在网络编程中,RingBuffer 常用于保存 socket 收到但还没处理完的数据,或者保存即将发送但还没写完的数据比如一次read只读到半个 HTTP 请求,就可以先放进 RingBuffer,等下一次数据到来后再继续解析。
2026-05-13 22:07:59
334
原创 基于时间堆实现毫秒级定时器
在服务器中,定时器常用于连接超时检测、心跳检测、延迟任务执行等场景如果每次tick都遍历所有定时器,那么定时器数量越多,检查成本越高这样每次只需要检查堆顶定时器如果堆顶还没有到期,那么堆中其他定时器一定也没有到期如果堆顶已经到期,就删除堆顶并执行回调,然后继续检查新的堆顶。
2026-05-13 17:36:04
378
原创 基于时间轮实现毫秒级定时器
在服务器开发中,定时器经常用于处理连接超时、心跳检测、资源清理、缓存刷新等任务如果使用排序链表管理定时器,每次添加新定时器都要按照过期时间找到插入位置,定时器数量多时插入效率会下降可以把时间轮理解成一个环形数组,数组中的每个位置叫做一个槽,也就是slot每个槽后面挂一条定时器链表,时间轮指针每隔一段时间向后移动一格,这一次移动叫做一次tick。
2026-05-13 15:51:22
409
原创 RPC 异步调用基本使用方法:基于官方helloworld-async 示例
上一篇已经介绍过 RPC、gRPC、Protobuf、HTTP/2、Channel、Stub、Service、ServerBuilder 这些基础概念,所以这里不再重复展开简单回顾一句:gRPC 的基本开发流程就是先写.proto文件定义服务和消息,再用protoc生成 C++ 代码,服务端实现服务,客户端通过Stub发起远程调用同步调用的特点是写法直观,客户端调用 RPC 后阻塞等待结果,服务端像普通函数一样重写接口、填充响应、返回Status;
2026-05-04 01:22:49
471
2
原创 RPC 同步调用基本使用方法:基于官方 RouteGuide 示例
同步 gRPC 的第一步是写.proto文件。.proto文件不是普通 C++ 代码,而是接口描述文件,它负责描述传输的数据结构和远程调用接口。例如一个最简单的服务可以写成,它表示客户端发送一个,服务端返回一个HelloReply。在 C++ 中不能直接编译.proto文件,需要通过protoc生成和,前者主要对应 Protobuf 消息类,后者主要对应 gRPC 服务类和 Stub 类在 RouteGuide 项目中,定义了一个RouteGuide这四行就是整个项目的接口核心GetFeature。
2026-05-04 00:59:47
454
原创 protobuf 使用详解
MOBILE = 0;HOME = 1;WORK = 2;Person:单个联系人AddressBook:电话簿,内部保存多个 PersonPersonname:姓名id:联系人 IDemail:邮箱phones:多个电话号码last_updated:最后更新时间。
2026-05-02 17:40:52
712
原创 C++ 多态详解:从静态多态到动态多态
从字面上理解,多态就是“多种形态”。在程序设计里,它指的是:使用,却可以对做出不同的具体行为。virtual。
2025-12-04 15:53:14
1175
原创 C/C++中的extern关键字详解
像上面讲的那样,编译期编译器只需要知道这些变量/函数是定义在其他地方的,链接的时候将extern_main.o和extern_other.o链接在一起,就可以找到这些变量/函数的具体地址。所以extern关键字的作用就是在编译期告诉编译器,这个变量/函数是其他地方定义的,此时编译器也不需要直到具体是在哪里定义的,链接阶段会找到extern关键字修饰的变量/函数的真正地址。extern是c/c++语言中的一个关键字,用在变量或函数前面,作用是说明这个变量或函数是在别处定义的,要在此处引用。
2025-11-06 13:55:46
499
原创 C++中malloc、free和new、delete的区别
malloc、free是c语言中的库函数内存操作malloc函数接收需要分配的内存字节数,如果内存足够,可以成功分配,会返回指向内存块起始位置的指针free函数释放指针指向的内存,接收的参数是需要释放的内存的首地址用法实际上,在调用malloc之后,返回给用户的指针指向的地址的前面还有一段元数据,这部分元数据记录了内存块的大小等数据元数据的大小是固定的,所以在调用free函数的时候会先找到这部分元数据,获得内存块大小之后再执行释放内存。
2025-11-04 21:07:20
399
原创 c/c++ static关键字详解
static关键字作用在函数体内部的变量,也就是局部静态变量不同的局部变量存储在栈中而被static修饰的局部变量存储在静态存储区中,即使函数退出,他也不会被销毁上面例子可以发现,a和b的地址是紧邻的,而和他们声明在一起的c的地址则没有普通局部变量在进入函数时创建,退出函数时销毁静态局部变量在程序启动时分配内存,在程序结束时释放,它的生命周期贯穿整个程序局部静态变量,名字已经很清楚了,他的作用域只能是在这个函数内部,函数外是无法访问的。但是每次调用这个函数的时候,访问的都是同一个静态变量。
2025-11-02 13:38:56
470
原创 c/c++ const关键字详解
const修饰基础数据类型的变量,如,就是声明一个常量,不能修改该变量的值c语言中,const只是一种编译器约束,它告诉编译器:“不要通过变量名修改这个变量的值”,但是并没有实现真正的只读内存保护,c语言中是可以通过指针修改const变量的值的,甚至可以通过强制类型转换修改const变量,但这些都是未定义行为而c++ 中const具有更强的类型约束和访问保护,编译器通常会将全局或静态const对象放入只读内存区(但标准并未强制要求),从而在语义上和物理上都防止修改。
2025-10-31 18:23:36
365
原创 红黑树详解
普通二叉树的删除底层节点的删除直接删除,没有影响非底层节点找左子树最大或右子树最小来代替自己,最后删除底层节点所以发现,二叉树的删除其实都发生在底层最后需要明确,红黑树的删除,第一步也是将对于非底层节点的删除进行转换,将其与其前驱(左子树最大节点)或者后继(右子树最小节点)进行交换,此时就会变成删除底层节点或者删除一个只有一个子节点的节点。
2025-10-17 21:43:29
832
原创 Redis(四)——Redis主从同步与对象模型
当redis管理的内存达到了上限,想要继续操作但是没有了内存,此时会触发淘汰机制,可能会淘汰部分数据来写入新数据,但默认策略是不淘汰任何数据,返回错误给客户端。
2025-10-09 20:52:32
657
原创 Redis(二)——Redis协议与异步方式
普通的redis交互(非pipeline):(1)客户端发送一条命令(2)redis服务器执行命令并返回结果(3)客户端收到结果后发送下一条命令pipeline:允许客户端将多条命令一次性发送给服务器,而不是一条一条,redis会依次执行这些命令并将结果一次性返回:pipeline不具备事务性。
2025-10-08 17:03:48
1101
1
原创 Linux网络编程基础API
字节序分和可以使用下面程序判断自己电脑的字节序现代PC多采用,因此小端字节序又被称为。因此大端字节序也被称为下面四个函数可以进行主机字节序和网络字节序之间的转换上面几个函数中,h代表host本地,n代表network网络,l代表long32位数字,s代表short16位数字。所以htonl就表示将32位本地字节序整数转网络字节序。
2025-08-25 20:30:55
712
原创 《UNIX环境高级编程》Chapter11——线程
典型的UNIX进程可以看成只有一个控制线程:一个进程在某个时刻只能做一件事情。有了多线程之后,程序设计就可以把进程设计成:在某个时刻能做不止一件事情,每个线程处理各自独立的任务多线程不等于多核编程。即使在单核处理器上,也能够使用多线程;对于多线程任务,如果某个线程阻塞了,那么其他的线程也可以执行,而不会像单线程那样阻塞整个进程;每个线程都包含有表示执行环境所必须的所有信息,包括:进程中标识线程的线程ID、一组寄存器值、栈、调度优先级和策略、信号屏蔽字、errno变量、以及线程私有数据;
2025-08-24 21:41:41
456
原创 tcpdump使用讲解
加上具体某个协议的某个端口,就可以更加精确的捕获对应的数据包。:对标准输出进行行缓冲,使标准输出折别遇到一个换行符就马上把这行内容打印出来,在需要同时观察抓包打印以及保存抓包记录时很有用。:count,指定收到数据包的次数,即在收到指定数量的数据包之后退出tcpdump,停止抓包。:在输出行打印除数据链路层的头部信息,默认情况下不显式数据链路层的信息,使用。如果你非常明确关心的是哪个主机的收发报文,就可以直接过滤那台主机的数据包。:write,将捕获到的信息保存到文件中,不打印在屏幕中。
2025-08-24 17:05:02
1211
原创 《UNIX环境高级编程》Chapter5——标准I/O库
如若在未定向的流上使用一个多字节IO函数,就将流定向为多字节,如若使用一个单字节的,就定向为单字节。流的定向决定了所读写的字符是单字节还是多字节的。对于二进制IO操作,我们可能更多的用于读写一个结构,此时按行读写或者按字符读写都不太合适,一次读写一整个结构更合适。记录文件位置,这种数据类型可以根据需要定义为一个足够大的数用于记录文件位置;,与上面两个的区别是文件偏移量不再是长整型,变成了。标准IO提供缓冲的目的是减少系统调用的次数,如。,用于将流设置到文件的起始位置。,文件的位置存放在一个长整型。
2025-08-18 10:15:53
294
原创 《UNIX环境高级编程》Chapter3——文件I/O
有缓冲的IO,数据读写先经过用户空间的缓冲区,等缓冲区满了或手动刷新,才调用底层的系统调用;:没有缓冲的IO,每次进行读写,直接发起系统调用;二者的区别在于有缓冲的IO进行系统调用的次数明显少于无缓冲IO,从而效率更高;本文要讲的文件IO属于无缓冲的IO。
2025-08-15 21:37:25
915
转载 转载:Linux内核Makefile系统文件详解
什么是makefile?或许很多Winodws的程序员都不知道这个东西,因为那些Windows 的IDE都为你做了这个工作,但我觉得要作一个好的和professional的程序员,makefile 还是要懂。这就好像现在有这么多的HTML的编辑器,但如果你想成为一个专业人士,你还是要了解HTML的标识的含义。特别在Unix下的软件编译,你就不能不自己写makefile了,会不会写makefile,从一个侧面说明了一个人是否具备完成大型工程的能力。因为, makefile关系到了整个工程的编译规则。
2025-08-07 11:18:53
93
原创 Linux基础命令
与more功能类似的还有less,less比more功能更强大,less可以通过。pwd,print work directory,输出当前的工作目录。mkdir,make directory,创建目录/文件夹。which,查看所使用的命令的程序文件存放的位置。tail,查看文件尾部内容,跟踪文件最新更改。more,查看文件内容,支持翻页查看。rm,remove,删除文件/文件夹。查看下一页,不支持查看上一页,点击。cp,copy,复制文件/文件夹。mv,move,移动文件/文件夹。cat,查看文件内容。
2025-07-14 13:02:52
706
原创 详解内存池
将程序中需要经常使用的核心资源先申请出来,放到一个池内,有程序自管理,这样可以提高资源的利用率,也可以保证本程序占有的资源数量,经常使用的池化技术包括内存池,线程池,和连接池等;:内存池是一种用于管理和分配内存的技术,它通过预先申请一块固定大小的内存空间,并将其划分为多个小块,提供给程序按需分配和释放。
2025-07-13 15:18:44
505
原创 《Effective C++》条款4:确定对象被使用前已被初始化
为内置型对象进行手工初始化,因为c++不保证会初始化他们构造函数使用初始化列表来初始化成员变量,而不要在构造函数内对他们另外赋值。初始化列表中变量的出现顺序尽量和他们的声明顺序相同为免除“跨编译单元的初始化次序”问题,使用的方式以对象替换对象。
2025-03-06 21:49:16
1089
原创 《Effective C++》条款3:尽可能使用const
将某些东西声明为const可帮助编译器侦测出错误用法。const可被施加于任何作用域内的对象、函数参数、函数返回类型、成员函数本体编译器强制实施,但你编写程序时应该使用“概念上的常量性”当const和non-const成员函数有着实质等价的实现时,令non-const版本调用const版本可以避免代码重复。
2025-03-05 18:10:54
850
原创 详解线索二叉树
二叉树本身不是一种线性结构,但是通过遍历,我们却可以将其导出为一个线性序列,在这个线性序列中,每个节点(除第一个和最后一个)都有它的和,通过每个节点的前驱和后继,我们也可以很快的得到这棵树的一个线性序列,所以将前驱和后继称为想要快速找到每一个节点的前驱和后继,我们可以在每个节点中加两个信息,指向前驱的指针和指向后继的指针,这样做的问题是浪费大量空间;发现,原来节点的leftChild和rightChild指针其实有很多是没有利用起来的,我们可以在这些空指针域中存储前驱和后继。
2024-11-22 16:20:36
1001
空空如也
空空如也
TA创建的收藏夹 TA关注的收藏夹
TA关注的人
RSS订阅