腾讯瑞驰 C++ 后端一面:线程池、TCP/UDP、分布式锁与 B+ 树索引
- 轮次
- 一面
- 时间
- 2026-10
- 来源
- 牛客网
《面试题目》
- C++ 自己没有线程池的库吗?你用过 C++ 自研线程池吗?
- TCP 和 UDP 这两个协议有什么差异?三次握手和四次挥手的过程呢?
- MCP 到底是给大模型用的,还是给 Agent 用的?
- 链表逆序,写出完整代码,迭代和递归两种实现分别怎么写?
- 给定一棵二叉树和两个指定节点,找到它们的最近公共祖先;先写递归版,再讲不用递归的完整思路。
- 简单讲一下封装、继承和多态,它们分别有什么作用?
- C++ 多态的底层是怎么实现的?虚函数和虚析构函数分别起什么作用?
- 悲观锁和乐观锁有什么区别?
- 分布式锁要怎么做?说说常见的几种实现方案;Redis 分布式锁存在哪些缺陷,又该怎么解决?
- 聚簇索引和非聚簇索引有什么区别?
- 主键和唯一键有什么差异?
- MySQL 的 left join、right join 和 inner join 有什么区别?
- MySQL 索引为什么采用 B+ 树,用 B+ 树有什么好处?
- 哈希索引为什么不适合 MySQL 的常规场景,不能拿它做主索引?
- MySQL 的事务隔离级别有哪些?MVCC 是怎么实现的?
- MySQL 索引相关:什么是回表,什么是覆盖索引?
《参考解析》
C++ 线程池:C++11 起标准库只有 std::thread、mutex、condition_variable、future 这些原语,没有现成线程池,std::async 也不是池。工程里常用 Boost.Asio 的 thread_pool、TBB,或自己写:任务队列加固定数量 worker,用互斥量和条件变量唤醒,析构时置停止标志并 notify_all,worker 取完剩余任务再退出。要补三点:队列有界并做背压、异常不能逃出 worker、需要返回值就用 packaged_task 配 future。
TCP 与 UDP:TCP 面向连接、可靠、有序、面向字节流,有流量控制和拥塞控制;UDP 无连接、不保证可靠和顺序、保留报文边界,头部只有 8 字节,适合 DNS、实时音视频和游戏这类宁可丢帧也不等重传的场景。三次握手是 SYN、SYN+ACK、ACK,核心是同步初始序列号并确认双方收发能力;四次挥手是 FIN、ACK、FIN、ACK,因为全双工,两个方向要分别关闭,主动关闭方最后等 2MSL 进 TIME_WAIT,保证最后的 ACK 到达并让旧报文消散。
MCP:MCP 是模型宿主或 Agent 运行时接外部工具和数据的标准协议。它连接 MCP Client(Claude Desktop、IDE、Agent 框架)和 MCP Server,模型本身不直接说 MCP;Server 暴露 tools、resources、prompts,宿主再转成模型可调用的函数。所以准确说法是「给 Agent 用,最终让大模型用上外部能力」。底层走 JSON-RPC 2.0,常见传输是本地 stdio 和远程 Streamable HTTP,价值在协议统一、工具可复用。
链表逆序与二叉树 LCA:链表迭代用 prev、cur、next 三个指针,每步先存 next,把 cur->next 指向 prev,再整体后移,O(n) 时间、O(1) 空间;递归是先把 head->next 之后逆序,再让 head->next->next = head、head->next = nullptr,空间 O(n)。LCA 递归版是后序遍历:左右子树分别找,两边都非空则当前节点即答案,只有一边非空就返回那一边。非递归可用栈求根到两节点的路径,比较最后一个相同节点;也可记录父指针,从一个节点往上打标记,另一个往上走,第一次遇到标记点就是 LCA。要说明「一个节点是另一个的祖先」和「节点不在树中」两个边界。
分布式锁与 Redis 实现:常见三种:Redis 的 SET key value NX PX、ZooKeeper 临时顺序节点、数据库唯一索引或行锁。Redis 版要讲清:加锁一条命令原子完成;value 放唯一 requestId;解锁用 Lua 先比对再删除,防止误删;业务可能超时就用看门狗续期。缺陷有单机故障、主从异步复制丢锁、TTL 与业务耗时不确定、客户端长时间 GC 后锁已过期;对应解法是主从加 WAIT、Redlock(有争议)、fencing token 让下游拒绝过期持有者,或换 etcd、ZooKeeper 这类强一致组件。