《Linux操作系统编程详解》《笔试/面试常见算法:从基础到进阶》《Python干货分享》
🎬 艾莉丝的简介:
文章目录
- 1 ~> ChatServer 启动与停止模块
- 1.1 业务背景与前置条件
- 1.2 头文件定义(类声明)
- 1.2.1 类成员说明
- 1.3 start () 服务启动接口实现
- 1.3.1 核心逻辑流程
- 1.4 stop () 服务停止接口实现
- 1.4.1 核心逻辑流程
- 1.5 isRunning () 状态查询接口实现
- 1.6 ChatServer 构造函数关键初始化逻辑
- 2 ~> 关键技术要点与坑点总结
- 2.1 为什么必须放到子线程执行 listen
- 2.2 为什么使用 std::atomic,不使用普通 bool
- 2.3 detach 线程注意事项
- 2.4 接口调用时序约束
- 3 ~> 面试核心考点
- 结尾
1 ~> ChatServer 启动与停止模块
基于 C++ + httplib 库实现 HTTP 聊天服务,解决服务启停、主线程阻塞、多线程并发状态安全问题
1.1 业务背景与前置条件
- 前置工作:模型配置完成,聊天 SDK 完成初始化;基于
httplib::Server构建 HTTP 服务实例 - 需要实现 3 个核心接口:服务启动、服务停止、运行状态检测
- 核心痛点:
httplib::Server::listen()为阻塞调用,直接在主线程调用会阻塞整个程序,必须将服务监听放置在独立子线程执行 - 并发风险:启停跨线程读写运行状态标记变量,普通 bool 会产生数据竞争,必须使用原子变量保证线程安全
1.2 头文件定义(类声明)
1.2.1 类成员说明
- 构造函数:接收服务器配置结构体
ServerConfig - 析构函数:资源回收
- 对外成员函数
bool start():启动 HTTP 服务,子线程运行监听逻辑void stop():停止 HTTP 服务bool isRunning() const:获取服务当前运行状态
- 私有成员
ServerConfig _config:保存传入的服务 IP、端口配置std::unique_ptr<httplib::Server> _chatServer:httplib 服务智能指针对象std::shared_ptr<ai_chat_sdk::ChatSDK> _chatSDK:聊天 SDK 智能指针std::atomic<bool> _isRunning{false}:原子布尔标记,标记服务运行状态,解决多线程读写竞争
#pragmaonce#include<memory>#include<atomic>#include<httplib.h>#include"server_config.h"#include"ai_chat_sdk.h"structServerConfig{std::string host;uint16_tport;};namespaceai_chat_server{classChatServer{public:ChatServer(constServerConfig&config);~ChatServer();boolstart();// 启动服务器voidstop();// 停止服务器boolisRunning()const;// 获取运行状态private:ServerConfig _config;std::unique_ptr<httplib::Server>_chatServer=nullptr;std::shared_ptr<ai_chat_sdk::ChatSDK>_chatSDK=nullptr;std::atomic<bool>_isRunning{false};// 原子变量,多线程安全标记运行状态};}1.3 start () 服务启动接口实现
1.3.1 核心逻辑流程
- 使用
_isRunning.load()原子读操作,检测服务是否已经运行;若正在运行打印错误日志直接返回 false - 创建
std::thread子线程,lambda 捕获 this 指针,线程内部执行httplib::Server::listen()阻塞监听 - 调用
detach()分离线程,子线程独立运行,与主线程解耦,主线程不会被 listen 阻塞 - 使用
_isRunning.store(true)原子写,标记服务已启动 - 打印启动日志,返回 true 代表启动调用成功
注意:
detach()只是分离线程,不代表 listen 监听一定成功;detach 之后主线程无法 join 回收该线程资源。listen 内部会处理端口绑定失败,错误日志会在子线程输出。
#include"ChatServer.h"#include<thread>#include"logger.h"namespaceai_chat_server{boolChatServer::start(){// 原子加载:检测服务是否已经处于运行状态if(_isRunning.load()){ERR("ChatServer is running!!!");returnfalse;}// 创建子线程,将阻塞的listen监听放到子线程执行,避免阻塞主线程std::threadserverThread([this]()->void{// httplib阻塞监听,绑定配置中的IP与端口_chatServer->listen(_config.host,_config.port);INFO("ChatServer start on {}:{}",_config.host,_config.port);});// 线程分离,子线程独立运行生命周期,主线程不再管理该线程serverThread.detach();// 原子写:标记服务已经启动_isRunning.store(true);INFO("ChatServer start success!!!");returntrue;}}1.4 stop () 服务停止接口实现
1.4.1 核心逻辑流程
_isRunning.load()读取原子状态,如果服务未运行,打印错误日志直接返回- 校验
_chatServer指针有效性,调用httplib::Server::stop()终止 HTTP 监听 _isRunning.store(false)原子写,标记服务停止- 输出停止日志
底层原理:
httplib::Server::stop()会主动终止 listen 阻塞调用,退出子线程内部的监听循环;detach 分离后的子线程执行完 listen 退出后自动回收。
namespaceai_chat_server{voidChatServer::stop(){// 原子加载读取运行状态if(!_isRunning.load()){ERR("ChatServer is not running!!!");return;}// 调用httplib库接口停止服务if(_chatServer){_chatServer->stop();}// 原子写,更新运行标记为停止_isRunning.store(false);INFO("ChatServer stop success!!!");}}1.5 isRunning () 状态查询接口实现
- 直接调用
load()原子读操作返回内部状态,const 修饰保证不修改对象
namespaceai_chat_server{boolChatServer::isRunning()const{return_isRunning.load();}}1.6 ChatServer 构造函数关键初始化逻辑
namespaceai_chat_server{ChatServer::ChatServer(constServerConfig&config):_config(config){// 创建httplib服务实例_chatServer=std::make_unique<httplib::Server>();if(!_chatServer){ERR("ChatServer init Failed!!!");return;}// 此处完成ChatSDK初始化逻辑}}2 ~> 关键技术要点与坑点总结
2.1 为什么必须放到子线程执行 listen
httplib::Server::listen()是同步阻塞 API,调用后函数不会返回,直到调用 stop ()、发生异常出错- 如果直接在主线程调用 listen,主线程被挂起,程序无法执行其他业务逻辑,无法调用 stop 完成关闭
2.2 为什么使用 std::atomic,不使用普通 bool
- start 在主线程修改
_isRunning,子线程库内部也会间接访问,stop 也会修改;多线程同时读写普通 bool 变量会触发数据竞争,属于 C++ 未定义行为 std::atomic<bool>提供原子读写:.load():原子读,获取变量值.store(val):原子写,写入变量值
- 禁止直接对原子变量使用
=赋值,规范使用 load/store 接口
2.3 detach 线程注意事项
detach()之后主线程不能 join 该线程,子线程运行结束后操作系统自动回收资源- 风险点:对象提前析构,如果 ChatServer 对象被销毁,而子线程还在执行 lambda 内部逻辑,会发生野指针访问崩溃;业务层要保证对象生命周期大于服务子线程生命周期。
2.4 接口调用时序约束
- 先构造 ChatServer 对象,完成内部
_chatServer初始化 - 调用
start()启动服务 - 可调用
isRunning()查询状态 - 退出前调用
stop()停止服务,再销毁 ChatServer 对象
3 ~> 面试核心考点
- httplib listen 阻塞特性,如何解决主线程阻塞问题
- 多线程共享状态变量为什么要使用 atomic,数据竞争概念
- std::thread detach 的作用、优缺点、潜在风险
- start/stop/isRunning 整套接口的线程安全设计思路
- httplib::stop 底层行为,stop 调用后 listen 如何退出阻塞
结尾
uu们,本文的内容到这里就全部结束了,艾莉丝在这里再次感谢您的阅读!
|
结语:希望对学习Linux相关内容的uu有所帮助,不要忘记给博主“一键四连”哦!
往期回顾:
【AI大模型接入SDK】ChatServer HTTP接口模块实现总览
🗡博主在这里放了一只小狗,大家看完了摸摸小狗放松一下吧!🗡 ૮₍ ˶ ˊ ᴥ ˋ˶₎ა