制作网站的步骤和过程wordpress 发布软件

当前位置: 首页 > news >正文

制作网站的步骤和过程,wordpress 发布软件,杭州今天查出多少阳性,网站建设与网络编辑心得体会#x1f3af; C 网络编程(12)服务器逻辑层单例模式设计 #x1f4c5; 更新时间#xff1a;2025年6月15日 #x1f3f7;️ 标签#xff1a;C | Boost.Asio | 网络编程 | 单例模式 | 并发 | 加锁 文章目录 前言一、什么是单例模式#xff1f;二、单例模板类1.代码部分:2.细… C 网络编程(12)服务器逻辑层单例模式设计 更新时间2025年6月15日 ️ 标签C | Boost.Asio | 网络编程 | 单例模式 | 并发 | 加锁 文章目录 前言一、什么是单例模式二、单例模板类1.代码部分:2.细节点详解问题1问题2问题3问题4关键点 问题5 三、LogicSystem单例类1.LogicSystem类2.代码详解 四、LogicNode封装消息节点五、实现LogicSystem.cpp总结 前言 前文第11章我们完善了消息节点使用了tlv协议的格式去发送消息 今天我们来实现用单例模式来实现逻辑类 一、什么是单例模式 单例模式Singleton Pattern 是一种创建型设计模式它的核心目的是 ✅ 保证一个类在整个程序中只能有一个实例 ✅ 并且提供一个全局访问点来获取这个实例 通俗理解 你可以把“单例”理解成 程序里的 “唯一老大”这个类你只能创建一次。 再创建不行得给你原来的那个 就像你电脑系统里的「任务管理器」只能打开一个 二、单例模板类 接下来我们实现一个单例模板类因为服务器的逻辑处理需要单例模式后期可能还会有一些模块的设计也需要单例模式所以先实现一个单例模板类然后其他想实现单例类只需要继承这个模板类即可 1.代码部分: #pragma once #includememory #includemutex #includeiostream using namespace std;templatetypename T class Singleton { protected:Singleton() default;Singleton(const SingletonT) delete;Singleton operator (const SingletonT st) delete;static std::shared_ptrT _instance;public:static std::shared_ptrT GetInstance(){static std::once_flag s_flag;std::call_once(s_flag, {_instance shared_ptrT(new T);//_instancestd::make_sharedT();//错误})return _instance;}//打印地址void PrintAddress(){cout _instance.get() endl;}~Singleton(){cout this is Singleton destruct endl;} };templatetypename T std::shared_ptrT SingletonT::_instance nullptr;//外部定义2.细节点详解 问题1 为什么要将 默认构造和拷贝构造和拷贝赋值写在protected权限下 因为我们要做的是单例模式这样可以禁止外部随意创建或复制对象 Singleton(const SingletonT) delete可以禁止下面这种情况发生 SingletonMyClass s2 s1;然后这样写 Singleton operator(const SingletonT) delete禁止别人写 s2 s1; 拷贝赋值同样可能让多个对象共享不一致状态也破坏了单例性质 问题2 默认构造函数的写法问题 下面两种写法区别在哪 Singleton() default;Singleton(){}虽然都是默认构造函数而且什么都没有但第一个是默认第二个是用户自己写的 前者真正调用编译器生成的默认实现 保留了默认构造函数的所有特性比如更高的性能、更强的编译器优化 Singleton() default; 是在明确告诉编译器“我就是要用默认构造函数不加任何逻辑” Singleton() {} 虽然也“能用”但从语义上看不清楚你是故意要自定义、还是写了个空的 问题3 为什么成员变量智能指针对象 _instance和 成员函数GetInstance都要用static静态变量 核心 因为 单例模式的目标 是 “类只存在一个全局唯一对象且不需要实例化这个类就能访问它” 而 static 恰好实现了这个目标 下面这个写法 保证了这个类全局只有一份实例指针 static std::shared_ptrT _instance;然后后面定义的函数也是静态的因为一开始我们没有实例化对象也可以直接调用 static std::shared_ptrT GetInstance()问题4 为什么要用 std::once_flag 和 std::call_once ? 这两个是什么 ? 这两个东西是 C11 引入的用于线程安全保证代码只执行一次的标准工具。特别适合像单例模式这种 “只创建一个实例” 的场景防止多线程同时初始化造成的问题 std::once_flag 是一个轻量级的标记用来标识某段代码是否已经执行过。 它是一个不可复制的对象只能和 std::call_once 配合使用 你可以把它想象成“这段代码是否执行过”的一个开关 std::call_once 是一个函数模板接受两个参数 一个 std::once_flag 变量标记 一个函数或可调用对象lambda、函数指针等 std::call_once 会保证传入的函数在多线程中只执行一次 也就是说不管多少线程同时调用 call_once只有第一个线程会执行函数体其他线程会等待或者跳过确保代码只执行一次 我们结合代码来总结这里 static std::once_flag s_flag; // 定义标记变量只会有一份std::call_once(s_flag, {_instance shared_ptrT(new T); // 只执行一次的初始化代码 }); 当多个线程同时调用 GetInstance()所有线程都会尝试执行 call_once。 call_once 看到 s_flag 还没被“打开”第一个线程会执行 lambda 里的代码创建 _instance。 其他线程看到 s_flag 已经“打开”就不会再重复创建 _instance直接跳过。 这样避免了竞态条件和重复创建 看到这里大家可能会有一个问题 为什么不直接用互斥锁 std::mutex 因为互斥锁每次都会锁住和解锁可能带来额外开销。 call_once 内部实现是高度优化的只在第一次调用时做加锁之后就不会再加锁性能更好 关键点 这里有一个很关键的地方 static std::once_flag s_flag;此处必须用static 来定义这个变量 GetInstance() 是一个静态成员函数里面定义了 static std::once_flag s_flag;意味着 s_flag 这个标志是函数内部的静态局部变量。 静态局部变量只会初始化一次而且在程序整个生命周期内存在不管你调用多少次 GetInstance()都用的是同一个 s_flag 如果你不写 statics_flag 就变成了普通局部变量每次调用 GetInstance() 时都会新建一个根本没法记录“这个代码块执行过没”call_once 失去作用线程安全就没法保证了 问题5 为什么在创建_instance的时候这种写法是错误的 _instancestd::make_sharedT();//错误因为我们使用make_shared的时候内部会自动调用 T 的构造函数但我们一开始将构造函数设置为 private 导致无法调用构造函数 所以我们用下面这种写法 _instance shared_ptrT(new T);三、LogicSystem单例类 我们实现逻辑系统的单例类继承自SingletonLogicSystem这样LogicSystem的构造函数和拷贝构造函数就都变为私有的了因为基类的构造函数和拷贝构造函数都是私有的。另外LogicSystem也有了基类的成员_instance和GetInstance函数。从而达到单例效果 1.LogicSystem类 #pragma once #includeSingleton.h #includequeue #includethread #includeCSession.h #includemap #includefunctional #includeconst.h #includejson/json.h #includejson/value.h #includejson/reader.htypedef std::functionvoid(shared_ptrCSession, short msg_id, string msg_data) FunCallBack;class LogicSystem:public SingletonLogicSystem {friend class SingletonLogicSystem;public:~LogicSystem();void PostMsgToQue( shared_ptrLogicSystemmsg );private:LogicSystem();void Dealmsg();void RegisterCallBacks();void HelloWordCallBack(shared_ptrCSession, short msg_id, string msg_data);std::thread _worker_thread;std::queueshared_ptrLogicNode _msg_que;std::mutex _mutex;std::condition_variable _consume;bool _b_stop;std::mapshort, FunCallBack _fun_callbacks; }; 2.代码详解 FunCallBack为要注册的回调函数类型其参数为绘画类智能指针消息id以及消息内容 _msg_que为逻辑队列 _mutex 为保证逻辑队列安全的互斥量 _consume表示消费者条件变量用来控制当逻辑队列为空时保证线程暂时挂起等待不要干扰其他线程。 _fun_callbacks表示回调函数的map根据id查找对应的逻辑处理函数。 _worker_thread表示工作线程用来从逻辑队列中取数据并执行回调函数。 _b_stop表示收到外部的停止信号逻辑类要中止工作线程并优雅退出。 LogicNode定义在CSession.h中 我们这一步就是在做我们之前说过的将回调中要处理的逻辑一个个放入队列中一个个处理
我们对代码的知识点进行讲解 我们这里用typedefstd::function简化函数 typedef std::functionvoid(shared_ptrCSession, short msg_id, string msg_data) FunCallBack;意思是将返回值为void并且有这三个相应参数的函数变成一个模板函数然后取名为 FunCallBack 这里举个例子 #include iostream #include functional #include memory using namespace std;class CSession {};typedef functionvoid(shared_ptrCSession, short, string) FunCallBack;// 一个实际的回调函数 void MyHandler(shared_ptrCSession session, short id, string data) {cout 处理消息: id , 内容: data endl; }int main() {FunCallBack cb MyHandler; // 把函数赋值给变量cb(make_sharedCSession(), 100, Hello); // 像函数一样调用 } 输出: 处理消息: 100, 内容: Hello这样就简化了调用函数 std::condition_variable 是 C 标准库中的一个同步原语属于 condition_variable 头文件。用于在多线程编程中实现线程之间的条件等待和通知机制常常与互斥锁如 std::mutex一起使用。 主要功能 等待条件线程可以在某个条件未满足时阻塞等待其他线程通知。 通知当条件满足时另一个线程可以唤醒等待的线程 四、LogicNode封装消息节点 class LogicNode {friend class LogicSystem; public:LogicNode(shared_ptrCSession session, shared_ptrRecvNode recvnode); private:shared_ptrCSession _session;shared_ptrRecvNode _recvnode; };LogicNode用于封装一个消息节点包含会话信息CSession和接收到的消息数据RecvNode 它作为 LogicSystem 处理的消息队列 _msg_que 的元素负责传递消息和上下文 五、实现LogicSystem.cpp 构造函数中我们将_b_stop初始化为false 意味着外部没有通知服务器关闭 然后我们调用注册函数将消息id和回调函数绑定这样后续触发的时候会调用回调函数 //构造函数: LogicSystem::LogicSystem():_b_stop(false){RegisterCallBacks();_worker_thread std::thread (LogicSystem::DealMsg, this); }//注册函数 void LogicSystem::RegisterCallBacks() {//当处理 MSG_HELLO_WORD 这个消息的时候就会调用 HelloWordCallBack这个回调函数//这里的MSG_HELLO_WORD 是1001 为消息id_fun_callbacks[MSG_HELLO_WORD] std::bind(LogicSystem::HelloWordCallBack, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3); }因为我们写的这个回调函数HelloWordCallBack有三个参数所以这里需要三个占位符 下面的回调函数就是解析信息和id 然后再将id和消息发送回去 //回调函数 void LogicSystem::HelloWordCallBack(shared_ptrCSessionsession, const short msg_id, const string msg_data) {Json::Reader reader;Json::Value root;reader.parse(msg_data, root);std::cout receive msg id is root[id].asInt() msg data is root[data].asString() std::endl;//回复给客户端root[data] server has receive msg,msg data is root[data].asString();std::string return_str root.toStyledString();session-Send(return_str, root[id].asInt()); }同时我们注意我们在构造函数中启动一个线程 _worker_thread std::thread (LogicSystem::DealMsg, this);这个DealMsg就是用来监听消息队列 _msg_que当有消息到达时取出并处理 void LogicSystem::Dealmsg() {for (;;){//加锁std::unique_lockstd::mutex unique_lk(_mutex);//判断队列为空while (_msg_que.empty() !_b_stop){_consume.wait(unique_lk);//先释放资源再唤醒}//取出所有数据 及时处理 退出循环if (_b_stop){while (!_msg_que.empty()){auto msg_node _msg_que.front();cout recv msg id is msg_node-_recvnode-_msg_id endl;auto call_back_iter _fun_callbacks.find(msg_node-_recvnode-_msg_id);if (call_back_iter _fun_callbacks.end()){_msg_que.pop();continue;}call_back_iter-second(msg_node-_session, msg_node-_recvnode-_msg_id,std::string(msg_node-_recvnode-_data, msg_node-_recvnode-_cur_len));_msg_que.pop();}break;}//如果继续 队列中还有数据auto msg_node _msg_que.front();cout recv msg id is msg_node-_recvnode-_msg_id endl;auto call_back_iter _fun_callbacks.find(msg_node-_recvnode-_msg_id);if (call_back_iter _fun_callbacks.end()){_msg_que.pop();continue;}call_back_iter-second(msg_node-_session, msg_node-_recvnode-_msg_id,std::string(msg_node-_recvnode-_data, msg_node-_recvnode-_cur_len));_msg_que.pop();} }大致流程就是通过加锁然后判断队列中是否有元素如果有就通过消息id去找相对应的回调函数看能否找到找不到就弹出并继续找到就调用回调函数传入对应的 _session、_msg_id 和从 _data 构造的字符串 最后还有封装了一个投递函数就是将消息投进消息队列中 void LogicSystem::PostMsgToQue(shared_ptrLogicNode msg) {//加锁std::unique_lockstd::mutex unique_lk(_mutex);_msg_que.push(msg);if (_msg_que.size() 1)//由0变1{_consume.notify_one();//唤醒} }因为我们在处理队列信息的时候当队列为空我们会停止但后续又有消息来的时候如果队列大小从0-1那我们要唤醒不让其一直卡住 总结 最后正常收发信息 本文实现了服务器的逻辑类包括并发控制等手段 ❤️ 如果你觉得本文对你有帮助欢迎点赞、评论与收藏。更多 c asio网络编程 开发知识敬请关注后续更新