
1) 【一句话结论】:采用基于状态机的线程安全用户会话管理机制,通过互斥锁保护状态变更,结合动态线程池按负载动态调整线程数,并实现Redis+数据库的持久化,确保高并发下状态一致性与资源高效利用。
2) 【原理/概念讲解】:老师口吻,解释用户会话状态管理需求。比如用户可能同时登录(状态切换到“登录中”)和提交作业(状态切换到“提交中”),多个线程同时修改状态会导致竞态(比如一个线程刚把状态改成“提交中”,另一个线程又改回“登录中”)。所以用互斥锁(std::mutex)保护状态修改的临界区,确保一次只有一个线程修改状态(类比银行账户,多线程存取钱时必须锁住账户,避免余额错误)。对于动态线程池,根据当前并发量(比如活跃用户数超过1000时,增加线程池线程数;低于500时减少),通过线程复用机制(预先创建线程池,负载增加时复用现有线程,减少创建开销),忙时多线程并行处理请求,闲时减少线程,节省资源。用户会话状态需要持久化(如Redis缓存+数据库备份),服务器重启后能从存储加载状态,避免状态丢失。对于读多写少场景(如用户信息查询),用**读写锁(std::shared_mutex)**替代互斥锁,提高读性能(比如100个线程读用户信息,1个线程写状态,读时共享锁,写时独占锁,减少锁竞争)。
3) 【对比与适用场景】:
同步原语对比:
| 同步原语 | 定义 | 特性 | 使用场景 | 注意点 |
|---|---|---|---|---|
| 互斥锁 | 独占访问临界区 | 互斥,一次只允许一个线程 | 状态修改(如登录、提交作业,写操作) | 锁粒度大,可能阻塞过多线程,影响读性能 |
| 读写锁 | 支持读多写一 | 读时共享,写时互斥 | 多读少写场景(如用户信息查询,读操作频繁) | 读多写少时效率高,减少锁竞争;写操作时阻塞所有线程 |
| 原子操作 | 无锁同步 | 轻量级,适用于轻量状态变更 | 状态标记(如登录状态标志,简单布尔值) | 仅适用于简单原子操作,复杂状态需锁 |
线程池类型对比:
| 线程池类型 | 线程数调整 | 适用场景 | 注意点 |
|---|---|---|---|
| 静态线程池 | 固定线程数 | 低负载,资源固定 | 负载变化时效率低,线程数可能不足或过剩 |
| 动态线程池(线程复用) | 根据负载动态调整,预先创建线程 | 高并发,负载波动大 | 需要同步机制,线程复用减少创建开销,但线程数变化过快可能影响性能 |
4) 【示例】(伪代码):
// 用户会话类(状态机+互斥锁+读写锁)
class UserSession {
private:
std::atomic<uint64_t> session_id_; // 原子ID,轻量标识
std::mutex session_mutex_; // 互斥锁保护状态(写操作)
std::string state_; // 状态:idle, login, watching, submitting
std::shared_mutex read_mutex_; // 读写锁,用于查询状态(读操作)
public:
UserSession() : state_("idle") {}
// 状态修改(写操作,用互斥锁)
void login() {
std::lock_guard<std::mutex> lock(session_mutex_);
state_ = "login";
// 登录逻辑...
}
void watchLive() {
std::lock_guard<std::mutex> lock(session_mutex_);
state_ = "watching";
// 直播逻辑...
}
void submitHomework() {
std::lock_guard<std::mutex> lock(session_mutex_);
state_ = "submitting";
// 提交作业逻辑...
}
// 状态查询(读操作,用读写锁)
std::string getState() const {
std::shared_lock<std::shared_mutex> lock(read_mutex_);
return state_;
}
};
// 动态线程池(线程复用)
class DynamicThreadPool {
private:
std::vector<std::thread> threads_; // 预先创建的线程池
std::queue<std::function<void()>> tasks_;
std::mutex tasks_mutex_;
std::condition_variable cv_;
bool stop_;
size_t target_threads_; // 目标线程数
public:
DynamicThreadPool(size_t initial_threads) : stop_(false), target_threads_(initial_threads) {
for (size_t i = 0; i < initial_threads; ++i) {
threads_.emplace_back([this] {
while (!stop_) {
std::function<void()> task;
{
std::unique_lock<std::mutex> lock(tasks_mutex_);
cv_.wait(lock, [this] { return !tasks_.empty() || stop_; });
if (stop_) break;
task = std::move(tasks_.front());
tasks_.pop();
}
task();
}
});
}
}
// 动态调整线程数(线程复用,减少创建开销)
void adjustThreads(size_t new_target) {
size_t current = threads_.size();
if (new_target > current) {
for (size_t i = current; i < new_target; ++i) {
threads_.emplace_back([this] {
while (!stop_) {
std::function<void()> task;
{
std::unique_lock<std::mutex> lock(tasks_mutex_);
cv_.wait(lock, [this] { return !tasks_.empty() || stop_; });
if (stop_) break;
task = std::move(tasks_.front());
tasks_.pop();
}
task();
}
});
}
} else if (new_target < current) {
for (size_t i = current; i > new_target; --i) {
threads_[i - 1].join();
threads_.erase(threads_.begin() + i - 1);
}
}
target_threads_ = new_target;
}
void addTask(std::function<void()> task) {
{
std::lock_guard<std::mutex> lock(tasks_mutex_);
tasks_.push(std::move(task));
}
cv_.notify_one();
}
};
5) 【面试口播版答案】:
“面试官您好,针对高并发用户会话管理,核心方案是构建基于状态机的线程安全机制,并配合动态线程池与持久化策略。首先,用户会话有多个状态(如空闲、登录中、观看直播、提交作业),这些状态变更需要线程安全,因此用互斥锁保护状态修改的临界区,比如登录操作加锁后修改状态为‘登录中’,避免多个线程同时修改导致状态混乱(就像银行账户,多线程存取钱必须锁住账户,否则余额错误)。对于动态线程池,根据当前活跃用户数动态调整线程数,比如当并发量超过1000时,增加线程池线程数,利用线程复用减少创建开销,忙时多线程并行处理请求,低于500时减少线程,节省资源。用户会话状态通过Redis RDB/AOF与数据库双备份持久化,服务器重启后从存储加载状态,避免状态丢失。对于读多写少场景(如查询用户信息),用读写锁替代互斥锁,提高读性能,比如100个线程同时查询状态,只阻塞写操作,减少锁竞争。这样既能保证线程安全,又能高效应对高并发请求。”
6) 【追问清单】:
7) 【常见坑/雷区】: