1. 问题背景与现象分析
在开发基于Qt+Boost.Asio+Protobuf的多人实时画板系统时,我遇到了一个极其隐蔽的并发问题。系统架构采用LogicServer(控制面)与多CanvasServer(数据面)分离的设计,每个CanvasServer负责处理实时绘图数据的传输和同步。
问题现象表现为:当新用户加入房间时,服务器会突然崩溃,调用栈显示在GetQueuedCompletionStatus处中断,CRT调用了abort()。最令人困惑的是,日志记录完全正常,所有业务逻辑看起来都正确执行。
关键提示:这种IO层级的崩溃往往比逻辑错误更难排查,因为它不遵循常规的错误模式,且通常与底层并发控制机制有关。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 系统架构与发送模型解析
2.1 核心架构设计
CanvasServer采用典型的实时服务器架构:
- 基于io_context的线程池处理IO事件
- 每个用户会话(Session)绑定独立socket连接
- 高频绘图消息(DrawReq)通过快速通道直接广播
- 低频控制消息(JoinRoom)由LogicSystem处理
- 房间维护历史记录(history)用于断线重连时的状态恢复
2.2 原始发送模型实现
初始的发送模型采用常见的"发送队列+链式写"设计:
cpp复制void CSession::Send(const std::string& msg, short msg_id) {
std::lock_guard<std::mutex> lock(_send_mutex);
_send_queue.push(send_node);
if (_send_queue.size() == 1) {
async_write(...); // 启动写链
}
}
这种设计理论上应该:
- 通过互斥锁保护发送队列
- 仅在队列为空时启动新的异步写操作
- 通过回调机制维持写操作的链式执行
3. 问题触发场景分析
问题的直接触发点是在实现历史记录回放功能时:
cpp复制// 新用户加入房间时回放历史笔迹
for (const auto& data : history_snapshot) {
sessi
