腾讯 微信客户端-C++ 二面

1、你做的高性能日志系统里最有挑战的是什么

答案:高性能日志系统里最有挑战的部分通常不是把日志写出去,而是在高并发场景下同时兼顾吞吐、时延和落盘可靠性。

如果每条日志都直接加锁写文件,线程一多就会在锁和系统调用上卡住,所以一般会把日志写入分成前台生产和后台刷盘两段。前台线程只负责把日志快速写入缓冲区,后台线程批量落盘。这样可以把大量小写合并成少量顺序写,减少系统调用和磁盘抖动。

真正难的地方在于几个细节。第一是缓冲区切换时机,切得太频繁会增加刷盘压力,切得太慢又会拖高日志可见延迟。第二是满载场景下的回压策略,如果生产速度远大于消费速度,日志队列迟早会堆爆,这时要决定阻塞业务线程、丢弃低优先级日志,还是降级打印。第三是崩溃前的数据保全,如果进程异常退出,缓冲区里还有没刷出去的数据,要考虑刷盘策略、刷盘周期和可靠性之间怎么平衡。

如果继续做优化,还会把时间格式化、线程 ID 拼接、字符串拼装这些高频开销提前处理,减少真正写日志路径上的动态分配和重复格式化。

2、项目拷打

3、无锁队列在高并发下是怎么工作的

答案:无锁队列主要依赖原子操作维护共享状态。在并发环境下,多个线程不会通过互斥锁串行进入临界区,而是通过 CAS 去尝试推进 head 或 tail 指针。

如果是单生产者单消费者队列,结构会简单很多,因为 push 和 pop 的修改职责天然分开。生产者只推进 tail,消费者只推进 head,不需要复杂的竞争协调。如果是多生产者多消费者队列,就要考虑 ABA、内存回收、伪共享和内存序的问题。比如一个节点出队以后,不能立刻释放,因为其他线程可能还保留着旧指针;CAS 成功后,新写入的节点内容也必须对其他线程及时可见。

代码:

#include <atomic>
using namespace std;

template <class T>
struct Node {
    T value;
    atomic<Node*> next{nullptr};
    Node(const T& v) : value(v) {}
};

template <class T>
class SPSCQueue {
public:
    SPSCQueue() {
        Node<T>* dummy = new Node<T>(T{});
        head_ = tail_ = dummy;
    }

    void push(const T& v) {
        Node<T>* node = new Node<T>(v);
        tail_->next.store(node, memory_order_release);
        tail_ = node;
    }

    bool pop(T& out) {
        Node<T>* next = head_->next.load(memory_order_acquire);
        if (!next) return false;
        out = next->value;
        delete head_;
        head_ = next;
        return true;
    }

private:
    Node<T>* head_;
    Node<T>* tail_;
};

4、乐观锁怎么理解,mutex 和自旋锁分别适合什么场景

答案:乐观锁的思路是先假设并发冲突不严重,修改共享数据时再做校验。如果状态没变,就提交;如果变了,就重试或者回滚。CAS 就是这种思路最常见的底层实现。

mutex 更适合锁持有时间不确定、临界区可能较长、甚至可能阻塞的场景。线程拿不到锁时通常会被挂起,让出 CPU。自旋锁更适合临界区很短、锁能很快释放的场景。拿不到锁的线程不睡眠,而是在用户态循环等待,避免线程切换。

如果竞争比较激烈,自旋锁会浪费大量 CPU;如果临界区很短但访问特别频繁,mutex 又可能在调度开销上显得偏重。

5、线程切换时到底切了什么

答案:线程切换时,操作系统需要先保存当前线程的执行现场,再恢复下一个线程之前保存的现场。

这里面包括通用寄存器、程序计数器、栈指针、状态寄存器,还可能包括浮点寄存器、SIMD 寄存器以及线程局部存储相关状态。除了 CPU 现场,还要切换线程在调度器里的状态,比如运行态、阻塞态、时间片、优先级等。

如果是同一个进程内的线程切换,地址空间一般不变;如果是不同进程之间切换,还会涉及页表切换和 TLB 干扰,代价更高。

6、栈上的对象是怎么析构的

答案:栈上对象的生命周期由作用域控制。编译器在生成代码时,会把析构调用插入到作用域结束的位置。正常路径上,执行流走到作用域末尾时,会按对象构造的逆序调用析构函数。

如果中途抛异常,编译器会配合异常展开机制,在栈展开过程中自动调用已经构造成功对象的析构函数,所以局部对象在异常场景里也能正常释放。

代码:

#include <iostream>
using namespace std;

class A {
public:
    A(const char* name) : name_(name) {
        cout << "construct " << name_ << 

剩余60%内容,订阅专栏后可继续查看/也可单篇购买

C++ 常考面试题总结 文章被收录于专栏

本专栏系统梳理C++方向, 大中厂高频高频面试考点 , 内容皆来自真实面试经历,从基础语法、内存管理、STL与设计模式,到操作系统与项目实战,结合真实面试题深度解析,帮助开发者高效查漏补缺,提升技术理解与面试通过率,打造扎实的C++工程能力.

全部评论
愿意试试多多吗,看我住叶,进度随时帮看
点赞 回复 分享
发布于 03-12 20:18 上海

相关推荐

刚刷到字节跳动官方发的消息,确实被这波阵仗吓了一跳。在大家还在纠结今年行情是不是又“寒冬”的时候,字节直接甩出了史上规模最大的转正实习计划——ByteIntern。咱们直接看几个最硬的数,别被花里胡哨的宣传词绕晕了。首先是“量大”。全球招7000多人是什么概念?这几乎是把很多中型互联网公司的总人数都给招进来了。最关键的是,这次的资源分配非常精准:研发岗给了4800多个Offer,占比直接超过六成。说白了,字节今年还是要死磕技术,尤其是产品和AI领域,这对于咱们写代码的同学来说,绝对是今年最厚的一块肥肉。其次是大家最关心的“转正率”。官方直接白纸黑字写了:整体转正率超过50%。这意味着只要你进去了,不划水、正常干,每两个人里就有一个能直接拿校招Offer。对于2027届(2026年9月到2027年8月毕业)的同学来说,这不仅是实习,这简直就是通往大厂的快捷通道。不过,我也得泼盆冷水。坑位多,不代表门槛低。字节的实习面试出了名的爱考算法和工程实操,尤其是今年重点倾斜AI方向,如果你简历里有和AI相关的项目,优势还是有的。而且,转正率50%也意味着剩下那50%的人是陪跑的,进去之后的考核压力肯定不小。一句话总结:&nbsp;27届的兄弟们,别犹豫了。今年字节这是铁了心要抢提前批的人才,现在投递就是占坑。与其等到明年秋招去千军万马挤独木桥,不如现在进去先占个工位,把转正名额攥在手里。
喵_coding:别逗了 50%转正率 仔细想想 就是转正与不转正
字节7000实习来了,你...
点赞 评论 收藏
分享
评论
点赞
1
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务