艾莉丝努力练剑头像
【AI大模型接入SDK】ChatServer 启动与停止模块实现封面图

【AI大模型接入SDK】ChatServer 启动与停止模块实现


头像


🎬 个人主页:艾莉丝努力练剑

❄专栏传送门:《C语言》《数据结构与算法》《C/C++干货分享&学习过程记录》
《Linux操作系统编程详解》《笔试/面试常见算法:从基础到进阶》《Python干货分享》

⭐️为天地立心,为生民立命,为往圣继绝学,为万世开太平

🎬 艾莉丝的简介:

在这里插入图片描述



在这里插入图片描述


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}:原子布尔标记,标记服务运行状态,解决多线程读写竞争
#pragma once
#include <memory>
#include <atomic>
#include <httplib.h>
#include "server_config.h"
#include "ai_chat_sdk.h"

struct ServerConfig
{
    std::string host;
    uint16_t port;
};

namespace ai_chat_server
{
class ChatServer
{
public:
    ChatServer(const ServerConfig& config);
    ~ChatServer();

    bool start();                 // 启动服务器
    void stop();                  // 停止服务器
    bool isRunning() 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 核心逻辑流程

  1. 使用_isRunning.load()原子读操作,检测服务是否已经运行;若正在运行打印错误日志直接返回 false
  2. 创建std::thread子线程,lambda 捕获 this 指针,线程内部执行httplib::Server::listen()阻塞监听
  3. 调用detach()分离线程,子线程独立运行,与主线程解耦,主线程不会被 listen 阻塞
  4. 使用_isRunning.store(true)原子写,标记服务已启动
  5. 打印启动日志,返回 true 代表启动调用成功

注意:detach()只是分离线程,不代表 listen 监听一定成功;detach 之后主线程无法 join 回收该线程资源。listen 内部会处理端口绑定失败,错误日志会在子线程输出。

#include "ChatServer.h"
#include <thread>
#include "logger.h"

namespace ai_chat_server
{
bool ChatServer::start()
{
    // 原子加载:检测服务是否已经处于运行状态
    if (_isRunning.load())
    {
        ERR("ChatServer is running!!!");
        return false;
    }

    // 创建子线程,将阻塞的listen监听放到子线程执行,避免阻塞主线程
    std::thread serverThread([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!!!");
    return true;
}
}

1.4 stop () 服务停止接口实现

1.4.1 核心逻辑流程

  1. _isRunning.load()读取原子状态,如果服务未运行,打印错误日志直接返回
  2. 校验_chatServer指针有效性,调用httplib::Server::stop()终止 HTTP 监听
  3. _isRunning.store(false)原子写,标记服务停止
  4. 输出停止日志

底层原理:httplib::Server::stop()会主动终止 listen 阻塞调用,退出子线程内部的监听循环;detach 分离后的子线程执行完 listen 退出后自动回收。

namespace ai_chat_server
{
void ChatServer::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 修饰保证不修改对象
namespace ai_chat_server
{
bool ChatServer::isRunning() const
{
    return _isRunning.load();
}
}

1.6 ChatServer 构造函数关键初始化逻辑

namespace ai_chat_server
{
ChatServer::ChatServer(const ServerConfig& 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 接口调用时序约束

  1. 先构造 ChatServer 对象,完成内部_chatServer初始化
  2. 调用start()启动服务
  3. 可调用isRunning()查询状态
  4. 退出前调用stop()停止服务,再销毁 ChatServer 对象

3 ~> 面试核心考点

  1. httplib listen 阻塞特性,如何解决主线程阻塞问题
  2. 多线程共享状态变量为什么要使用 atomic,数据竞争概念
  3. std::thread detach 的作用、优缺点、潜在风险
  4. start/stop/isRunning 整套接口的线程安全设计思路
  5. httplib::stop 底层行为,stop 调用后 listen 如何退出阻塞

结尾

uu们,本文的内容到这里就全部结束了,艾莉丝在这里再次感谢您的阅读!

艾莉丝努力练剑

C/C++ & Linux 底层探索者 | 一个正在努力练剑的技术博主


👀 【关注】 跟随我一起深耕技术领域,见证每一次成长。
❤️ 【点赞】 让优质内容被更多人看见,让知识传递更有力量。
⭐ 【收藏】 把核心知识点存好,在需要时随时查、随时用。
💬 【评论】 分享你的经验或疑问,评论区一起交流避坑!

不要忘记给博主“一键四连”哦!

“今日练剑达成!”
文章配图

“技术之路难免有困惑,但同行的人会让前进更有方向。”

结语:希望对学习Linux相关内容的uu有所帮助,不要忘记给博主“一键四连”哦!

往期回顾:

【AI大模型接入SDK】ChatServer HTTP接口模块实现总览

🗡博主在这里放了一只小狗,大家看完了摸摸小狗放松一下吧!🗡
૮₍ ˶ ˊ ᴥ ˋ˶₎ა

在这里插入图片描述

转载自 CSDN-专业IT技术社区

原文链接:https://blog.csdn.net/2401_89899187/article/details/167040452

文章来源转载

评论

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--