51mee - AI智能招聘平台Logo
模拟面试题目大全招聘中心会员专区

在C++中实现一个高并发请求处理模块(如用户登录、课程预约),请说明多线程模型、锁优化策略及线程池设计。

好未来后端 - C++难度:中等

答案

1) 【一句话结论】在C++实现高并发请求处理模块时,核心采用线程池+任务队列的生产者消费者架构,通过条件变量与互斥锁保障任务队列同步,结合读写锁优化用户登录等读多写少场景(内存锁与数据库锁按顺序获取避免死锁),并依据压力测试数据动态调整线程池阈值(如队列积压或空闲率超过阈值时扩缩容),任务队列满时按业务优先级(关键业务阻塞,非关键丢弃)处理,平衡性能与资源。

2) 【原理/概念讲解】老师口吻解释关键概念:

  • 多线程模型(线程池):预先创建线程池,任务队列存储请求,线程从队列取任务执行。类比工厂流水线,任务队列是待加工零件,线程是工人,线程池提前安排工人,减少等待。条件变量用于线程等待任务,互斥锁保护队列,防止并发修改。
  • 锁优化策略:读写锁适用于用户登录(读多写少)。读操作(查询用户信息)用共享锁,写操作(更新登录状态)用独占锁。与数据库锁协同时,内存读写锁与数据库行级锁需按顺序获取(读:内存共享锁→数据库读锁;写:内存独占锁→数据库写锁),否则可能导致死锁(如读线程持有内存读锁,数据库读锁,写线程持有内存独占锁,数据库写锁,顺序颠倒时死锁)。
  • 动态线程池调整:根据负载指标(如任务队列长度>100且持续5秒,或空闲线程>50%且持续3秒)动态增减线程。压力测试中,当QPS超过2000且响应时间>500ms时,增加线程;空闲线程超过60%时减少线程。调整频率每秒一次,避免频繁切换影响性能。
  • 任务队列同步:队列满时,阻塞生产者(等待队列空)适用于关键业务(如登录),避免请求丢失;丢弃任务适用于非关键业务(如课程预约),减少资源浪费。

3) 【对比与适用场景】

对比维度策略/类型定义特性使用场景注意点
线程池线程数固定大小预先创建固定数量线程线程数固定,资源占用稳定请求量稳定可能线程数不足或过剩
线程池线程数动态调整根据负载(队列长度、空闲率)增减线程线程数自适应请求量波动大需监控指标,增加复杂度
锁类型互斥锁独占访问读/写都阻塞其他线程写操作少,读操作多读线程等待写线程,性能下降
锁类型读写锁读共享,写独占读多写少场景用户登录(读多写少)写时阻塞读,读时允许写;需协调数据库锁避免死锁
锁类型自旋锁线程循环等待锁竞争轻时短时间持有锁CPU占用高,锁竞争重时性能差
任务队列无容量限制队列无大小限制生产者无阻塞请求量极低可能导致内存溢出
任务队列有容量限制队列设置最大容量生产者阻塞或丢弃任务请求量波动大需根据业务重要性选择策略(关键业务阻塞,非关键丢弃)
无锁实现CAS等无锁操作,线程安全高并发读,无锁竞争纯内存操作的高并发读场景需保证数据一致性(重试次数有限制),不适合涉及数据库的写操作

4) 【示例】(伪代码,含动态调整与锁协同)

// 动态线程池类(含同步原语)
class DynamicThreadPool {
private:
    std::queue<std::function<void()>> taskQueue;
    std::vector<std::thread> threads;
    std::atomic<bool> isRunning{true};
    std::mutex queueMutex;
    std::condition_variable cv;
    size_t minThreads = 4;
    size_t maxThreads = 32;
    size_t currentThreads = 8;
    std::atomic<size_t> idleThreads{0};

    void adjustThreadCount() {
        size_t queueSize = taskQueue.size();
        size_t idle = idleThreads.load();
        if (queueSize > 100 && currentThreads < maxThreads) {
            size_t newThreads = std::min(currentThreads + 2, maxThreads);
            for (size_t i = currentThreads; i < newThreads; ++i) {
                threads.emplace_back([this]() {
                    while (isRunning) {
                        std::function<void()> task;
                        {
                            std::unique_lock<std::mutex> lock(queueMutex);
                            cv.wait(lock, [this] { return !taskQueue.empty() || !isRunning; });
                            if (!isRunning && taskQueue.empty()) return;
                            task = std::move(taskQueue.front());
                            taskQueue.pop();
                        }
                        task();
                    }
                });
                currentThreads++;
            }
        } else if (idle > 0.5 * currentThreads && currentThreads > minThreads) {
            size_t newThreads = std::max(currentThreads - 2, minThreads);
            for (size_t i = currentThreads; i > newThreads; --i) {
                threads[i - 1].join();
                threads.pop_back();
                currentThreads--;
            }
        }
    }

public:
    DynamicThreadPool(size_t initThreads = 8) : currentThreads(initThreads) {
        for (size_t i = 0; i < initThreads; ++i) {
            threads.emplace_back([this]() {
                while (isRunning) {
                    std::function<void()> task;
                    {
                        std::unique_lock<std::mutex> lock(queueMutex);
                        cv.wait(lock, [this] { return !taskQueue.empty() || !isRunning; });
                        if (!isRunning && taskQueue.empty()) return;
                        task = std::move(taskQueue.front());
                        taskQueue.pop();
                    }
                    task();
                }
            });
        }
    }

    ~DynamicThreadPool() {
        isRunning = false;
        cv.notify_all();
        for (auto& t : threads) t.join();
    }

    void addTask(const std::function<void()>& task) {
        {
            std::lock_guard<std::mutex> lock(queueMutex);
            taskQueue.push(task);
        }
        cv.notify_one();
        adjustThreadCount();
    }
};

// 用户登录处理(读写锁+数据库锁协同)
void handleLogin(const std::string& username, const std::string& password) {
    std::shared_mutex userMutex;
    // 读操作(内存+数据库读)
    std::shared_lock<std::shared_mutex> readLock(userMutex);
    // 查询数据库:用户是否存在且密码正确(数据库读锁)
    // 写操作(内存+数据库写)
    std::unique_lock<std::shared_mutex> writeLock(userMutex);
    // 更新数据库:标记登录状态(数据库写锁)
    // 锁获取顺序:读时先内存共享锁,再数据库读锁;写时先内存独占锁,再数据库写锁,避免死锁
}

int main() {
    DynamicThreadPool pool(8);
    for (int i = 0; i < 1000; ++i) {
        pool.addTask([i]() {
            handleLogin("user" + std::to_string(i), "password");
        });
    }
    return 0;
}

5) 【面试口播版答案】(约90秒)
“面试官您好,针对高并发请求处理模块,我的核心思路是构建线程池+任务队列的生产者消费者架构,通过条件变量与互斥锁保障任务队列同步,结合读写锁优化用户登录等读多写少场景,并依据压力测试数据动态调整线程池阈值(如队列积压或空闲率超过阈值时扩缩容),任务队列满时按业务优先级(关键业务阻塞,非关键丢弃)处理。具体来说,线程池预先创建线程,任务队列存储请求,线程从队列取任务执行。对于用户登录,查询用户信息(读)用共享锁,更新登录状态(写)用独占锁,与数据库锁协同时按顺序获取(读:内存共享锁→数据库读锁;写:内存独占锁→数据库写锁),避免死锁。动态调整线程池时,当任务队列长度超过100个任务且持续5秒,或空闲线程超过当前线程数的50%且持续3秒,就增减线程,平衡资源与性能。任务队列设置容量限制,队列满时,登录等关键业务会阻塞生产者等待队列空后再处理,避免请求丢失;课程预约等非关键业务则丢弃任务,减少资源占用。这样既能保证高并发下的性能,又能优化资源使用。”

6) 【追问清单】

  • 问:线程池的动态调整阈值如何设定?
    答:通常通过压力测试,比如当队列积压超过100个任务且持续5秒,或空闲线程超过当前线程数的50%且持续3秒时,触发扩容或缩容,调整频率为每秒一次,避免频繁切换影响性能。
  • 问:读写锁与数据库锁协同的顺序如何避免死锁?
    答:读操作时先获取内存共享锁,再获取数据库读锁;写操作时先获取内存独占锁,再获取数据库写锁,确保所有线程按相同顺序获取锁,避免循环等待导致死锁。
  • 问:任务队列满时的处理策略如何区分业务优先级?
    答:根据业务重要性,关键业务(如登录)选择阻塞生产者,等待队列空后再处理,避免请求丢失;非关键业务(如课程预约)丢弃任务,减少资源占用,避免影响关键业务性能。
  • 问:无锁实现是否适用于用户登录?
    答:用户登录涉及数据库操作,无锁实现复杂,且数据库本身有锁机制,读写锁结合数据库锁更合适。无锁适用于纯内存操作的高并发读场景,不适合涉及数据库的写操作,因为无锁操作需保证数据一致性(如CAS失败后重试),否则可能导致数据错乱。
  • 问:动态调整线程池的线程回收是否会影响响应速度?
    答:线程空闲时间过长后回收,若任务突然增加,可能需要较长时间创建新线程,影响响应速度。可通过设置合理的空闲时间阈值(如10秒)或增加线程创建池来缓解,但需平衡资源占用与响应速度。

7) 【常见坑/雷区】

  • 线程池动态调整阈值设定不当:阈值过低导致线程数过多,上下文切换增加;阈值过高导致线程数不足,请求积压。需根据压力测试数据(如QPS、响应时间)确定阈值。
  • 读写锁与数据库锁的冲突:若内存锁与数据库锁不协调,可能导致死锁或数据不一致。需确保锁的获取顺序(读:内存共享锁→数据库读锁;写:内存独占锁→数据库写锁)。
  • 任务队列容量限制策略错误:关键业务选择阻塞生产者,避免请求丢失;非关键业务丢弃任务,可能导致用户体验差。需根据业务重要性选择策略。
  • 动态调整线程池的线程回收问题:线程空闲时间过长后回收,若任务突然增加,可能需要较长时间创建新线程,影响响应速度。可通过优化线程创建/销毁逻辑(如线程池复用)缓解。
  • 无锁实现的线程安全保证:无锁操作需保证数据一致性(如CAS失败后重试),否则可能导致数据错乱。适用于纯内存操作的高并发读场景,不适合涉及数据库的写操作。
51mee.com致力于为招聘者提供最新、最全的招聘信息。AI智能解析岗位要求,聚合全网优质机会。
产品招聘中心面经会员专区简历解析Resume API
联系我们南京浅度求索科技有限公司admin@51mee.com
联系客服
51mee客服微信二维码 - 扫码添加客服获取帮助
© 2025 南京浅度求索科技有限公司. All rights reserved.
公安备案图标苏公网安备32010602012192号苏ICP备2025178433号-1