C++编程:模拟实现CyberRT的DataVisitor和DataDispatcher

server/2024/12/2 12:07:39/

文章目录

    • 0. 引言
    • 1. 设计概要
      • 1.1 主要组件
      • 1.2 类关系图
      • 1.3 工作流程
    • 2. 代码实现

0. 引言

使用 C++ 实现一个类似CyberRT 架构的 DataVisitorDataDispatcher。在 CyberRT 中:

  • Receiver 接收到消息后,会触发回调。
  • 回调中调用 DataDispatcher(消息分发器)发布消息。
  • DataDispatcher 是一个单例,负责所有的数据分发,并将数据放入对应的缓存中。
  • 然后,DataDispatcher 会通知对应的协程(在此简化为线程)去处理消息。
  • DataVisitor(消息访问器)是辅助类,用于管理数据处理过程,包括注册通知机制和绑定回调函数。

1. 设计概要

1.1 主要组件

1.2 类关系图

以下类关系图,反映了 DataDispatcher 作为单例管理多个 DataVisitor,并与 Receiver 交互的关系。

管理
1
*
调用 Dispatch
Data
+int id
+std::string content
DataVisitor-13" transform="translate(408.1875, 592.5)">
«abstract»
+Notify(std::shared_ptr<data> data)
LoggingVisitor
+Create()
ProcessingVisitor
+Create()
DataDispatcher-16" transform="translate(408.1875, 331.5)">
-std::vector> visitors
-std::mutex mutex
+Instance()
+RegisterVisitor(std::shared_ptr visitor)
+UnregisterVisitor(std::shared_ptr visitor)
+Dispatch(std::shared_ptr<data> data)
Receiver
+ReceiveMessage(int id, const std::string& content)

1.3 工作流程

  1. Receiver 接收到消息后,调用 DataDispatcher::Instance()->Dispatch(data)
  2. DataDispatcher 将数据放入对应的 DataVisitor 的缓冲区。
  3. DataDispatcher 通知对应的 DataVisitor
  4. DataVisitor 的线程被唤醒,取出数据并执行绑定的回调函数进行处理。

2. 代码实现

完整代码见:data-visitor-dispatcher

2.1. 定义数据结构

首先,定义一个通用的数据结构 Data

// data.h
#ifndef DATA_H
#define DATA_H#include <string>// 定义一个通用的数据类型
struct Data {int id;std::string content;
};#endif  // DATA_H

DataVisitor_102">2.2. 实现 DataVisitor

DataVisitor 负责处理特定类型的数据。它包含一个线程,该线程等待 DataDispatcher 的通知,然后处理数据。

// data_visitor.h
#ifndef DATA_VISITOR_H
#define DATA_VISITOR_H#include <atomic>
#include <condition_variable>
#include <functional>
#include <memory>
#include <mutex>
#include <queue>
#include <thread>#include "data.h"class DataVisitor : public std::enable_shared_from_this<DataVisitor> {public:using Callback = std::function<void(std::shared_ptr<Data>)>;// 构造函数,绑定回调函数explicit DataVisitor(Callback callback) : callback_(callback), stop_flag_(false) {worker_thread_ = std::thread(&DataVisitor::ProcessData, this);}// 析构函数,确保线程安全停止~DataVisitor() {Stop();if (worker_thread_.joinable()) {worker_thread_.join();}}// 用于 DataDispatcher 发布数据时调用void Notify(std::shared_ptr<Data> data) {{std::lock_guard<std::mutex> lock(queue_mutex_);data_queue_.push(data);}cv_.notify_one();}private:// 数据处理线程函数void ProcessData() {while (!stop_flag_) {std::unique_lock<std::mutex> lock(queue_mutex_);cv_.wait(lock, [this]() { return stop_flag_ || !data_queue_.empty(); });while (!data_queue_.empty()) {auto data = data_queue_.front();data_queue_.pop();lock.unlock();// 执行绑定的回调函数if (callback_) {try {callback_(data);} catch (const std::exception& e) {// 处理回调中的异常,防止线程终止// 可以记录日志或采取其他措施// 这里简单输出错误信息std::cerr << "Exception in callback: " << e.what() << std::endl;}}lock.lock();}}}// 停止线程void Stop() {stop_flag_ = true;cv_.notify_all();}Callback callback_;std::thread worker_thread_;std::mutex queue_mutex_;std::condition_variable cv_;std::queue<std::shared_ptr<Data>> data_queue_;std::atomic<bool> stop_flag_;
};#endif  // DATA_VISITOR_H

DataDispatcher_194">2.3. 实现 DataDispatcher

DataDispatcher 是一个单例,负责管理所有的 DataVisitor 并分发数据。

// data_dispatcher.h
#ifndef DATA_DISPATCHER_H
#define DATA_DISPATCHER_H#include <algorithm>
#include <memory>
#include <mutex>
#include <vector>#include "data.h"
#include "data_visitor.h"// 单例的 DataDispatcher
class DataDispatcher {public:// 获取单例实例static DataDispatcher& Instance() {static DataDispatcher instance;return instance;}// 禁止拷贝和赋值DataDispatcher(const DataDispatcher&) = delete;DataDispatcher& operator=(const DataDispatcher&) = delete;// 注册一个 DataVisitorvoid RegisterVisitor(const std::shared_ptr<DataVisitor>& visitor) {std::lock_guard<std::mutex> lock(mutex_);visitors_.emplace_back(visitor);}// 注销一个 DataVisitorvoid UnregisterVisitor(const std::shared_ptr<DataVisitor>& visitor) {std::lock_guard<std::mutex> lock(mutex_);visitors_.erase(std::remove(visitors_.begin(), visitors_.end(), visitor), visitors_.end());}// 分发数据到所有注册的 DataVisitorvoid Dispatch(const std::shared_ptr<Data>& data) {std::lock_guard<std::mutex> lock(mutex_);for (auto& visitor : visitors_) {if (visitor) {visitor->Notify(data);}}}private:// 私有构造函数DataDispatcher() = default;~DataDispatcher() = default;std::vector<std::shared_ptr<DataVisitor>> visitors_;std::mutex mutex_;
};#endif  // DATA_DISPATCHER_H

2.4. 实现 Receiver

模拟消息接收器,每当接收到消息时,调用 DataDispatcher 分发数据。

// receiver.h
#ifndef RECEIVER_H
#define RECEIVER_H#include <functional>
#include <memory>
#include <string>#include "data.h"
#include "data_dispatcher.h"// Receiver,模拟消息接收器
class Receiver {public:using Callback = std::function<void(std::shared_ptr<Data>)>;// 构造函数,绑定回调函数explicit Receiver(Callback callback) : callback_(callback) {}// 模拟接收消息void ReceiveMessage(int id, const std::string& content) {auto data = std::make_shared<Data>();data->id = id;data->content = content;// 触发回调if (callback_) {callback_(data);}}private:Callback callback_;
};#endif  // RECEIVER_H

DataVisitor_302">2.5. 实现具体的 DataVisitor

例如,创建 LoggingVisitorProcessingVisitor,它们各自有不同的处理逻辑。

// logging_visitor.h
#ifndef LOGGING_VISITOR_H
#define LOGGING_VISITOR_H#include <iostream>
#include <memory>
#include "data_visitor.h"// LoggingVisitor,负责记录数据
class LoggingVisitor {public:// 创建一个 LoggingVisitor 的 DataVisitor 实例static std::shared_ptr<DataVisitor> Create() {return std::make_shared<DataVisitor>([](std::shared_ptr<Data> data) {std::cout << "[LoggingVisitor] Received data: ID=" << data->id << ", Content=\"" << data->content << "\""<< std::endl;});}
};#endif  // LOGGING_VISITOR_H
// processing_visitor.h
#ifndef PROCESSING_VISITOR_H
#define PROCESSING_VISITOR_H#include <iostream>
#include <memory>
#include "data_visitor.h"// ProcessingVisitor,负责处理数据
class ProcessingVisitor {public:// 创建一个 ProcessingVisitor 的 DataVisitor 实例static std::shared_ptr<DataVisitor> Create() {return std::make_shared<DataVisitor>([](std::shared_ptr<Data> data) {// 简单示例:打印数据长度std::cout << "[ProcessingVisitor] Processed data ID=" << data->id << ", Length=" << data->content.length()<< std::endl;});}
};#endif  // PROCESSING_VISITOR_H

2.6. 示例主程序

展示如何使用上述组件,实现数据接收、分发和处理。

// main.cpp
#include <chrono>
#include <iostream>
#include <memory>
#include <thread>#include "data_dispatcher.h"
#include "logging_visitor.h"
#include "processing_visitor.h"
#include "receiver.h"int main() {// 创建并注册 DataVisitorauto logger = LoggingVisitor::Create();auto processor = ProcessingVisitor::Create();DataDispatcher::Instance().RegisterVisitor(logger);DataDispatcher::Instance().RegisterVisitor(processor);// 创建 Receiver,并绑定 DataDispatcher 的 Dispatch 方法Receiver receiver([](std::shared_ptr<Data> data) { DataDispatcher::Instance().Dispatch(data); });// 模拟接收消息std::cout << "=== 接收第1条消息 ===" << std::endl;receiver.ReceiveMessage(1, "Hello, CyberRT!");std::cout << "=== 接收第2条消息 ===" << std::endl;receiver.ReceiveMessage(2, "Another data packet.");// 等待一段时间以确保所有消息被处理std::this_thread::sleep_for(std::chrono::seconds(1));// 注销一个 DataVisitorstd::cout << "\n=== 移除 LoggingVisitor ===" << std::endl;DataDispatcher::Instance().UnregisterVisitor(logger);// 再次接收消息std::cout << "=== 接收第3条消息 ===" << std::endl;receiver.ReceiveMessage(3, "Data after removing logger.");// 等待一段时间以确保消息被处理std::this_thread::sleep_for(std::chrono::seconds(1));std::cout << "\n=== 程序结束 ===" << std::endl;return 0;
}

2.7. 编译和运行

假设所有文件在同一目录下,可以使用以下命令进行编译:

g++ -std=c++14 main.cpp -o dispatcher -pthread

运行程序后,您将看到类似如下的输出:

test@pi:~/dataVisitor$ g++ -std=c++14 main.cpp -o dispatcher -pthread
test@pi:~/dataVisitor$ ./dispatcher 
=== 接收第1条消息 ===
=== 接收第2条消息 ===
[LoggingVisitor] Received data: ID=[ProcessingVisitor] Processed data ID=11, Content="Hello, CyberRT!"
[LoggingVisitor] Received data: ID=2, Content="Another data packet."
, Length=15
[ProcessingVisitor] Processed data ID=2, Length=20=== 移除 LoggingVisitor ===
=== 接收第3条消息 ===
[ProcessingVisitor] Processed data ID=3, Length=27=== 程序结束 ===

注意,第三条消息只被 ProcessingVisitor 处理,因为 LoggingVisitor 已被移除。


http://www.ppmy.cn/server/146708.html

相关文章

「Mac畅玩鸿蒙与硬件33」UI互动应用篇10 - 数字猜谜游戏

本篇将带你实现一个简单的数字猜谜游戏。用户输入一个数字&#xff0c;应用会判断是否接近目标数字&#xff0c;并提供提示“高一点”或“低一点”&#xff0c;直到用户猜中目标数字。这个小游戏结合状态管理和用户交互&#xff0c;是一个入门级的互动应用示例。 关键词 UI互…

Linux 系统目录结构

Linux 系统目录结构 登录系统后&#xff0c;在当前命令窗口下输入命令&#xff1a; ls / 你会看到如下图所示: 树状目录结构&#xff1a; 在 Linux 或 Unix 操作系统中&#xff0c;所有的文件和目录都被组织成以一个根节点开始的倒置的树状结构。 文件系统的最顶层是由根目…

React与Ant Design入门指南

创建基于React框架使用Ant Design组件库的技术文档时&#xff0c;我们需要涵盖从安装到使用的各个关键步骤。以下是一份简化的技术文档草稿&#xff0c;它旨在帮助开发者快速上手并开始构建界面。 React与Ant Design入门指南 1. 简介 Ant Design是一个致力于提供企业级UI设计…

Python入门(19)--最终篇

战略性学习与职业发展 &#x1f310; 引言&#xff1a;Python的战略价值 在当今快速evolving的技术景观中&#xff0c;Python已经成为跨领域应用最广泛的编程语言。从人工智能到web开发&#xff0c;从数据科学到云计算&#xff0c;Python展现出令人惊叹的适应性和影响力。 学…

AI前景分析展望——GPTo1 SoraAI

引言 人工智能&#xff08;AI&#xff09;领域的飞速发展已不仅仅局限于学术研究&#xff0c;它已渗透到各个行业&#xff0c;影响着从生产制造到创意产业的方方面面。在这场技术革新的浪潮中&#xff0c;一些领先的AI模型&#xff0c;像Sora和OpenAI的O1&#xff0c;凭借其强大…

初识Linux(4):Linux基础环境工具(下)

1. Git Git是一种版本控制系统&#xff0c;是一种工具&#xff0c;用于代码的存储和版本控制。 而我们常见的Gitee和Gitehub都是基于Git&#xff08;Git是开源的&#xff09;实现的在线代码仓库&#xff0c;而前者服务器位于中国&#xff0c;后者服务器位于美国。 总的来说&…

条件数:概念、矩阵中的应用及实际工业场景应用

一、引言 条件数是数值分析领域中的一个重要概念&#xff0c;它在理解线性方程组的敏感性、矩阵运算的稳定性等方面发挥着关键作用。无论是在纯数学理论研究&#xff0c;还是在解决实际工业问题的工程应用中&#xff0c;条件数都有着广泛的意义。 二、条件数的概念 &#xff…

混淆零碎知识点

minifyEnabled true //混淆开关 zipAlignEnabled true // Zipalign优化 shrinkResources true // 移除无用的resource文件 &#xff08;必须要混淆开了之后才才可以设置为true&#xff09; proguard-rules.pro 为混淆文件 //整个文件保留 不被混淆 -keep class com.cn…