
🎬 个人主页 :艾莉丝努力练剑
❄专栏传送门 :《C语言》《数据结构与算法》《C/C++干货分享&学习过程记录》
《Linux操作系统编程详解》《笔试/面试常见算法:从基础到进阶》《Python干货分享》
⭐️为天地立心,为生民立命,为往圣继绝学,为万世开太平
🎬 艾莉丝的简介:

文章目录
- [1 ~> LLM 会话管理模块整体介绍](#1 ~> LLM 会话管理模块整体介绍)
-
- [1.1 会话的核心概念](#1.1 会话的核心概念)
- [1.2 会话管理需要实现全部功能集合](#1.2 会话管理需要实现全部功能集合)
- [1.3 线程安全约束](#1.3 线程安全约束)
- [2 ~> 核心数据结构定义](#2 ~> 核心数据结构定义)
-
- [2.1 Message 消息结构体(common.h)](#2.1 Message 消息结构体(common.h))
- [2.2 Session 会话结构体(common.h)](#2.2 Session 会话结构体(common.h))
- [3 ~> SessionManager 会话管理类头文件设计(SessionManager.h)](#3 ~> SessionManager 会话管理类头文件设计(SessionManager.h))
-
- [3.1 存储容器选型分析](#3.1 存储容器选型分析)
- [3.2 完整头文件代码](#3.2 完整头文件代码)
- [4 ~> SessionManager 源文件实现(SessionManager.cpp)](#4 ~> SessionManager 源文件实现(SessionManager.cpp))
- [5 ~> 关键技术要点与面试考点](#5 ~> 关键技术要点与面试考点)
-
- [5.1 ID 生成逻辑](#5.1 ID 生成逻辑)
- [5.2 并发控制方案](#5.2 并发控制方案)
- [5.3 getSessionLists 会话列表排序逻辑](#5.3 getSessionLists 会话列表排序逻辑)
- [5.4 业务调用流程](#5.4 业务调用流程)
- [5.5 内存会话管理的局限与扩展方向](#5.5 内存会话管理的局限与扩展方向)
- [5.6 容器选型对比](#5.6 容器选型对比)
- [6 ~> 易错坑点总结](#6 ~> 易错坑点总结)
- 结尾

1 ~> LLM 会话管理模块整体介绍
1.1 会话的核心概念
- 会话定义:会话是用户与大语言模型之间一组连续交互集合,维护对话上下文、状态信息,保障对话连贯性与一致性。
- 模型侧特性 :大模型本身不会保存会话状态与历史上下文,会话数据必须由业务代码手动维护。
- 业务背景问题
- 多轮对话消息如何持久存储;
- 模型无记忆,调用模型时必须携带历史消息上下文;
- 区分多条消息归属哪一次对话;
- 在全部消息集合中快速提取某一次会话的全部历史。
1.2 会话管理需要实现全部功能集合
- 会话数据存储:维护
session_id <-> Session会话对象映射关系,用于快速查询会话。 - 创建会话:对话开始前新建会话,后续聊天消息全部归属该会话。
- 根据会话 ID 获取会话对象。
- 向指定会话追加消息(用户提问、模型回复)。
- 获取会话内全部历史消息,用于界面渲染、模型请求上下文组装。
- 获取全部会话 ID 列表,用于前端历史会话列表展示。
- 删除指定会话。
- 更新会话时间戳(创建时间、最后更新时间)。
- 清空全部会话。
- 获取当前会话总数量。
- 生成全局唯一会话 ID。
- 生成全局唯一消息 ID。
1.3 线程安全约束
- 触发并发风险场景:多用户同时访问服务,多线程同时执行创建会话、更新会话、删除会话操作。
- 风险现象:会话 ID 生成冲突、容器数据错乱、内存数据损坏。
- 解决方案:互斥锁保护共享容器;原子变量做计数;const 成员函数使用
mutable修饰锁变量。
2 ~> 核心数据结构定义
2.1 Message 消息结构体(common.h)
单条聊天消息,保存一条对话的完整信息。
C++
// common.h
#pragma once
#include <string>
namespace ai_chat_sdk {
struct Message {
std::string _messageId; // 消息唯一ID
std::string _role; // 角色 user / assistant / system
std::string _content; // 消息文本内容
Message(const std::string& role, const std::string& content)
: _role(role), _content(content)
{}
};
}
2.2 Session 会话结构体(common.h)
一次完整对话会话,包含会话元信息 + 该会话全部消息。
C++
// common.h
#pragma once
#include <string>
#include <vector>
#include <ctime>
#include <memory>
namespace ai_chat_sdk {
struct Session {
std::string _sessionId; // 会话唯一ID
std::string _modelName; // 当前会话使用的大模型名称
std::vector<Message> _messages; // 会话下全部消息
std::time_t _createdAt; // 会话创建时间戳
std::time_t _updatedAt; // 会话最后更新时间戳
explicit Session(const std::string& modelName = "")
: _modelName(modelName), _createdAt(std::time(nullptr)), _updatedAt(std::time(nullptr))
{}
};
}
3 ~> SessionManager 会话管理类头文件设计(SessionManager.h)
3.1 存储容器选型分析
- 选型:
std::unordered_map<std::string, std::shared_ptr<Session>> - key:
std::string会话 ID;value:会话对象智能指针 - 选择理由:底层哈希表,平均 O (1) 查询效率;不需要会话有序;使用
shared_ptr避免对象拷贝,堆分配减少容器内存膨胀。 - 不选用普通 map:map 底层红黑树,查询效率低于哈希表,会话 ID 有序性业务无需求。
3.2 完整头文件代码
C++
// sdk/include/SessionManager.h
#pragma once
#include <atomic>
#include <unordered_map>
#include <mutex>
#include <memory>
#include <string>
#include <vector>
#include "common.h"
namespace ai_chat_sdk {
class SessionManager {
public:
/// @brief 创建会话
/// @param modelName 会话使用模型名称
/// @return 返回新生成会话ID
std::string createSession(const std::string& modelName);
/// @brief 根据会话ID获取会话对象
/// @param sessionId 会话ID
/// @return 会话智能指针;不存在返回nullptr
std::shared_ptr<Session> getSession(const std::string& sessionId) const;
/// @brief 向指定会话追加消息
/// @param sessionId 目标会话ID
/// @param message 待添加消息
/// @return true成功;false会话不存在失败
bool addMessage(const std::string& sessionId, const Message& message);
/// @brief 获取会话全部历史消息
/// @param sessionId 会话ID
/// @return 消息vector,不存在返回空vector
std::vector<Message> getHistoryMessages(const std::string& sessionId) const;
/// @brief 更新会话最后更新时间戳
/// @param sessionId 会话ID
void updateSessionTimestamp(const std::string& sessionId);
/// @brief 获取全部会话ID列表,按会话更新时间戳降序排序(最新会话靠前)
/// @return 会话ID字符串数组
std::vector<std::string> getSessionLists() const;
/// @brief 删除指定会话
/// @param sessionId 待删除会话ID
/// @return true删除成功;false会话不存在
bool deleteSession(const std::string& sessionId);
/// @brief 清空内存中全部会话
void clearAllSessions();
/// @brief 获取当前会话总数
/// @return 会话数量
size_t getSessionCount() const;
private:
// 私有工具函数:生成会话唯一ID
std::string generateSessionId();
// 私有工具函数:生成消息唯一ID
std::string generateMessageId(size_t messageCounter);
private:
/// 全部会话存储容器:key=sessionId,value=会话智能指针
std::unordered_map<std::string, std::shared_ptr<Session>> _sessions;
/// 互斥锁;mutable修饰允许const成员函数内部修改锁状态
mutable std::mutex _mutex;
/// 会话计数器,原子类型保证多线程计数安全,初始值0
std::atomic<int64_t> _sessionCounter{0};
};
} // namespace ai_chat_sdk
关键知识点注解
mutable std::mutex _mutex:const 修饰成员函数不允许修改类成员变量;但锁在只读查询场景必须加锁保护,mutable 允许 const 函数修改该成员。std::atomic<int64_t> _sessionCounter:原子变量,多线程自增无锁安全,用于 ID 生成计数。- 返回会话列表只返回会话 ID 字符串集合:
- 避免大量 Session 对象拷贝,降低内存开销;
- 外部模块通过 ID 调用
getSession()获取对象,降低模块耦合; - 适配持久层 SQLite 数据库 CURD,数据库主键就是 sessionId。
4 ~> SessionManager 源文件实现(SessionManager.cpp)
C++
// sdk/src/SessionManager.cpp
#include "SessionManager.h"
#include <sstream>
#include <iomanip>
#include <algorithm>
#include <ctime>
namespace ai_chat_sdk {
// 生成会话ID,格式 session_时间戳_8位0填充计数
std::string SessionManager::generateSessionId()
{
// 原子变量线程安全自增
int64_t curCnt = _sessionCounter.fetch_add(1, std::memory_order_relaxed);
std::time_t now = std::time(nullptr);
std::ostringstream oss;
oss << "session_"
<< now
<< "_"
<< std::setw(8)
<< std::setfill('0')
<< curCnt;
return oss.str();
}
// 生成消息ID,格式 msg_时间戳_8位0填充计数
std::string SessionManager::generateMessageId(size_t messageCounter)
{
size_t curMsgCnt = messageCounter + 1;
std::time_t now = std::time(nullptr);
std::ostringstream oss;
oss << "msg_"
<< now
<< "_"
<< std::setw(8)
<< std::setfill('0')
<< curMsgCnt;
return oss.str();
}
std::string SessionManager::createSession(const std::string& modelName)
{
// lock_guard自动加解锁,离开作用域释放锁
std::lock_guard<std::mutex> lock(_mutex);
std::string sessionId = generateSessionId();
// 在堆上创建会话对象智能指针
auto sessionPtr = std::make_shared<Session>(modelName);
sessionPtr->_sessionId = sessionId;
// 存入哈希映射
_sessions[sessionId] = sessionPtr;
return sessionId;
}
std::shared_ptr<Session> SessionManager::getSession(const std::string& sessionId) const
{
std::lock_guard<std::mutex> lock(_mutex);
auto iter = _sessions.find(sessionId);
if (iter == _sessions.end())
{
return nullptr;
}
return iter->second;
}
bool SessionManager::addMessage(const std::string& sessionId, const Message& message)
{
std::lock_guard<std::mutex> lock(_mutex);
auto iter = _sessions.find(sessionId);
if (iter == _sessions.end())
{
return false;
}
auto& sessionPtr = iter->second;
// 根据当前会话消息数量生成消息ID
std::string msgId = generateMessageId(sessionPtr->_messages.size());
Message newMsg(message._role, message._content);
newMsg._messageId = msgId;
// 消息加入会话
sessionPtr->_messages.push_back(newMsg);
// 添加消息同时更新会话最后修改时间戳
sessionPtr->_updatedAt = std::time(nullptr);
return true;
}
std::vector<Message> SessionManager::getHistoryMessages(const std::string& sessionId) const
{
std::lock_guard<std::mutex> lock(_mutex);
auto iter = _sessions.find(sessionId);
if (iter == _sessions.end())
{
return {};
}
return iter->second->_messages;
}
void SessionManager::updateSessionTimestamp(const std::string& sessionId)
{
std::lock_guard<std::mutex> lock(_mutex);
auto iter = _sessions.find(sessionId);
if (iter == _sessions.end())
{
return;
}
iter->second->_updatedAt = std::time(nullptr);
}
std::vector<std::string> SessionManager::getSessionLists() const
{
std::lock_guard<std::mutex> lock(_mutex);
// 临时vector存储 <更新时间戳,会话智能指针对>
std::vector<std::pair<std::time_t, std::shared_ptr<Session>>> tempList;
tempList.reserve(_sessions.size());
for (const auto& item : _sessions)
{
tempList.emplace_back(item.second->_updatedAt, item.second);
}
// lambda比较器,按更新时间戳降序,最新会话排在前面
std::sort(tempList.begin(), tempList.end(),
[](const std::pair<std::time_t, std::shared_ptr<Session>>& a,
const std::pair<std::time_t, std::shared_ptr<Session>>& b)
{
return a.first > b.first;
});
// 提取排序完成后的会话ID
std::vector<std::string> resultIds;
resultIds.reserve(_sessions.size());
for (const auto& item : tempList)
{
resultIds.push_back(item.second->_sessionId);
}
return resultIds;
}
bool SessionManager::deleteSession(const std::string& sessionId)
{
std::lock_guard<std::mutex> lock(_mutex);
auto iter = _sessions.find(sessionId);
if (iter == _sessions.end())
{
return false;
}
_sessions.erase(iter);
return true;
}
void SessionManager::clearAllSessions()
{
std::lock_guard<std::mutex> lock(_mutex);
_sessions.clear();
}
size_t SessionManager::getSessionCount() const
{
std::lock_guard<std::mutex> lock(_mutex);
return _sessions.size();
}
} // namespace ai_chat_sdk
5 ~> 关键技术要点与面试考点
5.1 ID 生成逻辑
- 会话 ID :
session_时间戳_8位填充原子计数器;时间戳保证大范围区分,原子计数器解决同一秒内多个会话冲突。 - 消息 ID :
msg_时间戳_8位填充会话内消息计数;基于会话已有消息数量做计数,每个会话内部消息唯一。 - 使用
std::ostringstream+std::setw+std::setfill('0')做格式化字符串输出。
5.2 并发控制方案
- 共享容器
_sessions全部读写操作,使用std::lock_guard<std::mutex>自动管理锁生命周期,RAII 自动释放锁,避免异常场景死锁。 - 计数使用
std::atomic,简单计数操作不需要加互斥锁。 mutable关键字的使用场景:只读 const 成员函数,需要修改锁对象。
5.3 getSessionLists 会话列表排序逻辑
- 不能直接操作原 unordered_map;先拷贝时间戳与会话指针到临时 vector;
- 使用
std::sort配合 lambda 表达式实现按会话最后更新时间降序; - 提取排序完成的 sessionId 返回,前端展示历史会话列表时,最近交互会话放在列表顶部。
5.4 业务调用流程
- 用户新建对话:调用
createSession(modelName)获取 sessionId。 - 用户发送消息:调用
addMessage(sessionId, userMessage)保存用户消息。 - 获取该会话全部历史消息:调用
getHistoryMessages(sessionId),组装成大模型请求上下文。 - 获取模型回复后,再次调用
addMessage(sessionId, assistantMessage)保存模型回复,内部自动更新会话_updatedAt。 - 前端加载历史会话列表:调用
getSessionLists()获取有序会话 ID 数组,循环调用getSession()拿到会话元数据渲染 UI。 - 用户删除会话:调用
deleteSession(sessionId)。
5.5 内存会话管理的局限与扩展方向
当前实现全部会话保存在内存中,进程退出全部丢失; 工程化项目需要结合 SQLite 持久化:
- 创建会话、新增消息、删除会话同步写数据库;
- 程序启动时从数据库加载历史会话到内存
_sessions容器。
5.6 容器选型对比
| 容器 | 底层结构 | 查询复杂度 | 适用场景 |
|---|---|---|---|
std::unordered_map |
哈希表 | 平均 O (1) | 会话管理,key 随机字符串,不需要有序,优先选用 |
std::map |
红黑树 | O(logN) | 需要 key 有序排序场景,会话管理场景性能较差 |
6 ~> 易错坑点总结
- const 成员函数内部修改互斥锁,忘记加
mutable修饰_mutex,编译报错。 - ID 生成忘记原子变量,多线程并发创建会话出现 ID 重复冲突。
- addMessage 添加消息之后忘记更新会话
_updatedAt时间戳,会话列表排序错乱。 - 直接返回 Session 对象拷贝,造成巨大内存开销,模块耦合度上升;应当返回 sessionId + 提供 getSession 获取对象。
- 操作 unordered_map 迭代器失效风险:erase 操作不要使用失效迭代器;代码中使用 find 拿到迭代器之后,仅在锁保护范围内部操作。
- 排序时没有做临时拷贝,直接修改原 unordered_map,破坏哈希容器结构。
std::setfill('0')只对下一次输出生效,不能忘记搭配std::setw,否则不会补零。
结尾
uu们,本文的内容到这里就全部结束了,艾莉丝在这里再次感谢您的阅读!
|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| ### 艾莉丝努力练剑 C/C++ & Linux 底层探索者 | 一个正在努力练剑的技术博主 *** ** * ** *** 👀 【关注】 跟随我一起深耕技术领域,见证每一次成长。 ❤️ 【点赞】 让优质内容被更多人看见,让知识传递更有力量。 ⭐ 【收藏】 把核心知识点存好,在需要时随时查、随时用。 💬 【评论】 分享你的经验或疑问,评论区一起交流避坑! 不要忘记给博主"一键四连"哦! "今日练剑达成!"
"技术之路难免有困惑,但同行的人会让前进更有方向。" |
结语:希望对学习Linux相关内容的uu有所帮助,不要忘记给博主"一键四连"哦!
往期回顾:
【AI大模型接入SDK】LLMManager类架构与智能指针选型
🗡博主在这里放了一只小狗,大家看完了摸摸小狗放松一下吧!🗡 ૮₍ ˶ ˊ ᴥ ˋ˶₎ა
