蚂蚁 客户端开发-C++ 一面
1. 介绍一下 TCP 和 UDP 的区别和联系
TCP 和 UDP 都是传输层协议,都是建立在 IP 之上的端到端通信机制。TCP 面向连接,通信前需要建立连接,提供可靠传输、顺序保证、重传、流量控制和拥塞控制;UDP 是无连接的,不保证可靠到达,也不保证顺序,协议头更小,传输开销更低。如果业务更关注消息完整性和顺序,比如文件传输、RPC、数据库连接,通常用 TCP;如果更关注时延、实时性和实现灵活性,比如音视频、DNS、游戏状态同步,UDP 更常见。它们的联系在于都为上层应用提供传输能力,只是设计目标不同。TCP 更偏可靠,UDP 更偏轻量和快速。
2. C++ 中创建 socket 的过程是怎样的
如果是在 Linux 下,典型过程是先调用 socket 创建套接字,然后根据角色不同再继续。服务端通常会 bind 绑定地址和端口,listen 进入监听状态,再通过 accept 接收连接;客户端则一般是直接 connect 发起连接。如果要进行数据传输,常见接口是 send/recv 或者 read/write。通信结束后调用 close 释放文件描述符。如果放到工程里,还会涉及设置非阻塞、地址复用、心跳、超时、epoll 注册等步骤,实际不会只停留在几个基础 API。
代码:
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <cstring>
int main() {
int fd = socket(AF_INET, SOCK_STREAM, 0);
sockaddr_in addr{};
addr.sin_family = AF_INET;
addr.sin_port = htons(8080);
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
connect(fd, (sockaddr*)&addr, sizeof(addr));
const char* msg = "hello";
send(fd, msg, strlen(msg), 0);
close(fd);
return 0;
}
3. 为什么 malloc 在高并发场景下容易成为性能瓶颈,怎么优化
malloc 在高并发场景下的性能问题,核心通常不是“分配本身慢”,而是频繁分配释放带来的锁竞争、碎片化、系统调用成本和缓存局部性变差。多个线程同时申请内存时,底层分配器往往需要维护全局或部分共享元数据,这就容易产生竞争。再加上小对象分配特别频繁时,真正耗时的往往是同步和管理成本,而不是字节搬运。优化思路一般有几类:第一是对象池或内存池,把常见尺寸的小对象缓存起来复用;第二是线程本地缓存,减少跨线程竞争;第三是分级分配,把小对象和大对象分开处理;第四是尽量减少频繁创建销毁,往业务层面改成复用。
4. 线程本地缓存分配器一般是怎么设计的
常见设计是把内存分配做成三级结构:线程本地缓存、中心缓存、页级分配器。线程本地缓存负责处理当前线程最常见的小对象申请和释放,优点是绝大多数路径不需要加锁,速度很快。当某个 size class 的空闲对象不够时,再从中心缓存批量拿一批回来;中心缓存主要负责多个线程之间的内存流转。如果中心缓存也没有足够空间,就向更底层的页分配器申请更大的连续内存块,再切分成小对象。这种设计的关键点是批量搬运和按尺寸分类,不是每次都直接找系统要内存。
5. 为什么线程本地缓存里的哈希桶或者空闲链表很多时候不需要加锁
如果这个缓存明确只属于当前线程访问,那么它天然就避免了多线程并发修改同一份结构,因此不需要额外加锁。关键不是“哈希桶这个结构不需要锁”,而是“它的所有权是线程私有的”。只有当对象跨线程释放,或者线程本地缓存容量过大需要回收给中心缓存时,才会进入共享路径,那部分通常是需要同步的。所以面试里问这个问题,本质是在考线程局部存储和所有权边界,而不是单纯问数据结构。
6. 线程本地缓存是怎么做到线程独有的
最常见的方式就是 TLS,也就是线程局部存储。每个线程访问同一个“线程局部变量名”时,拿到的其实是自己那一份独立实例。这样分配器就可以把当前线程的本地缓存挂在线程局部变量上,访问时不需要在全局表里查找,也不需要额外加锁。线程结束时,对应的 TLS 对象也会跟着清理,线程本地缓存中的剩余资源就可以统一回收到中心缓存。本质上它解决的是“相同代码路径上,每个线程如何拥有自己的上下文状态”。
代码:
#include <iostream>
#include <thread>
using namespace std;
thread_local int tls_id = 0;
void work(int x) {
tls_id = x;
cout << this_thread::get_id() << " -> " << tls_id << endl;
}
int main() {
thread t1(work, 1);
thread t2(work, 2);
t1.join();
t2.join();
return 0;
}
7. TLS 在 C++ 中对应什么关键字,和 pthread_key_t 有什么区别
C++11 里对应的关键字是 thread_local。它是语言级支持,使用起来更自然,编译器会帮你处理对象的构造、析构和每线程实例管理。而 pthread_
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
本专栏系统梳理C++方向, 大中厂高频高频面试考点 , 内容皆来自真实面试经历,从基础语法、内存管理、STL与设计模式,到操作系统与项目实战,结合真实面试题深度解析,帮助开发者高效查漏补缺,提升技术理解与面试通过率,打造扎实的C++工程能力.