基于发布-订阅模型的多线程通信框架

文章介绍了一个使用C++11标准实现的简易多线程通信框架,它支持订阅和发布机制,允许线程间通过字符串消息进行通信。框架使用了条件变量、互斥锁等同步原语保证线程安全,并提供了通过回调函数处理接收到的消息的功能。示例代码展示了如何订阅主题并使用成员函数作为回调。

c++实现的一个简洁实用的多线程间通信的框架,利用chatGPT实现的😊,已通过测试。需支持c++11标准。实际使用中可以根据需要把数据包装成json格式的字符串再发布

multiThreadComm.h:

#ifndef _MULTITHREADCOMMS_H_
#define _MULTITHREADCOMMS_H_

#include <functional>
#include <unordered_map>
#include <mutex>
#include <condition_variable>
#include <set>
#include <queue>
#include <string>
#include <atomic>
#include <memory>

class MTComm
{
public:
    using Callback = std::function<void(const std::string &, void *context)>;

    // 获取单例实例
    static MTComm &getInst();

    // 禁止拷贝构造和赋值
    MTComm(const MTComm &) = delete;
    MTComm &operator=(const MTComm &) = delete;

    void subscribe(const std::string &topic, const std::string &subscriber, Callback callback, void *context);
    void unsubscribe(const std::string &topic, const std::string &subscriber);
    void publish(const std::string &topic, const std::string &message);

private:
    MTComm();  // 构造函数
    ~MTComm(); // 析构函数

    void run(); // 消息处理线程

private:
    bool running_;                                                                          // 运行状态标志
    std::mutex mtxsubs_, mtxmsgs_;                                                          // 互斥锁
    std::condition_variable cv_;                                                            // 条件变量
    std::unordered_map<std::string, std::pair<std::set<std::string>, void *>> subscribers_; // 订阅者列表
    std::queue<std::pair<std::string, std::string>> messages_;                              // 消息队列
    std::unordered_map<std::string, std::pair<Callback, void *>> callbacks_;                // 回调函数映射
};

#endif // _MULTITHREADCOMMS_H_

multiThreadComm.cpp:

#include "multiThreadComm.h"
#include <thread>
#include <iostream>

MTComm &MTComm::getInst()
{
    static MTComm instance; // 局部静态变量,保证实例唯一性
    return instance;
}

MTComm::MTComm() : running_(true)
{
    // 启动消息转发线程
    std::thread t(&MTComm::run, this);
    t.detach();
}

MTComm::~MTComm()
{
    // 停止消息转发线程
    running_ = false;
    cv_.notify_one();
}

// 不要在回调函数中调用subscribe或者unsubscribe,否则会造成死锁
void MTComm::subscribe(const std::string &topic, const std::string &subscriber, Callback callback, void *context)
{
    std::lock_guard<std::mutex> lock_subs(mtxsubs_); // 锁定订阅者互斥量
    if (callback)
    {
        auto it = subscribers_.find(topic);
        if (it != subscribers_.end())
        {
            auto &subs = it->second.first;
            if (subs.find(subscriber) == subs.end())
            {
                subs.insert(subscriber);                                               // 添加新订阅者
                callbacks_[subscriber] = std::make_pair(std::move(callback), context); // 存储回调
            }
        }
        else
        {
            std::set<std::string> subs;
            subs.insert(subscriber);
            // 新主题,新增订阅者和回调
            subscribers_[topic] = std::make_pair(subs, context);
            callbacks_[subscriber] = std::make_pair(std::move(callback), context);
        }
    }
}

void MTComm::unsubscribe(const std::string &topic, const std::string &subscriber)
{
    std::lock_guard<std::mutex> lock_subs(mtxsubs_); // 锁定订阅者互斥量
    auto it = subscribers_.find(topic);
    if (it != subscribers_.end())
    {
        std::set<std::string> &subs = it->second.first;
        subs.erase(subscriber);       // 移除订阅者
        callbacks_.erase(subscriber); // 移除回调
        if (subs.empty())
        { // 如果没有订阅者了,从映射表中移除该主题
            subscribers_.erase(it);
        }
    }
}

void MTComm::publish(const std::string &topic, const std::string &message)
{
    std::unique_lock<std::mutex> lock_msgs(mtxmsgs_); // 锁定消息互斥量
    messages_.push({topic, message});                 // 将新消息入队
    cv_.notify_one();                                 // 通知条件变量,唤醒等待线程
}

void MTComm::run()
{
    std::unique_lock<std::mutex> lock_msgs(mtxmsgs_); // 锁定消息互斥量

    while (running_)
    {
        // 等待有新消息或者被要求退出
        cv_.wait(lock_msgs, [this]()
                 { return !messages_.empty() || !running_; });
        if (!running_)
        {
            break; // 退出循环
        }
        // 转发消息
        while (!messages_.empty())
        {
            std::lock_guard<std::mutex> lock_subs(mtxsubs_); // 锁定订阅者互斥量
            auto message = messages_.front();                // 获取队头消息
            messages_.pop();                                 // 移除队头消息
            lock_msgs.unlock();                              // 释放消息锁

            auto it = subscribers_.find(message.first); // 查找主题
            if (it != subscribers_.end())
            {
                // 遍历所有订阅者
                for (const auto &subscriber : it->second.first)
                {
                    auto callbackIt = callbacks_.find(subscriber); // 查找回调
                    if (callbackIt != callbacks_.end())
                    {
                        // 调用回调函数
                        callbackIt->second.first(message.second, callbackIt->second.second);
                    }
                }
            }
            else
            {
                // 主题不存在,输出警告
                std::cout << "Warning: Topic not found - " << message.first << std::endl;
            }
            lock_msgs.lock(); // 重新锁定消息
        }
    }
}

使用demo,把类的成员函数传入回调的两种方法,当然也可以直接传入全局函数

#include <iostream>
#include <thread>
#include "multiThreadComm.h"

class MyClass
{
public:
    void onMessageReceived(const std::string &message, void *context)
    {
        std::cout << "Received message: " << message << std::endl;
        if (context != nullptr)
        {
            int *p_num = reinterpret_cast<int *>(context);
            std::cout << "num: " << *p_num << std::endl;
        }
    }
};

void thread1()
{
    // 发布消息
    MTComm::getInst().publish("my-topic1", "Hello, subscriber1!");
    MTComm::getInst().publish("my-topic2", "Hello, subscriber2!");
}

int main()
{
    MyClass myObj;

    /* 订阅主题 */
    // 使用lambda表达式将myObj的成员函数传入回调
    MTComm::getInst().subscribe("my-topic1", "my-subscriber1", [&myObj](const std::string &message, void *context)
                                { myObj.onMessageReceived(message, context); }, nullptr);

    // 使用std::bind将myObj的成员函数传入回调
    int *num = new int(10);
    MTComm::getInst().subscribe("my-topic2", "my-subscriber2",
                                std::bind(&MyClass::onMessageReceived, &myObj, std::placeholders::_1, std::placeholders::_2), num);

    std::thread t1(thread1);

    // 等待消息被处理
    std::this_thread::sleep_for(std::chrono::seconds(1));

    t1.join();
    delete num;

    return 0;
}

输出:

Received message: Hello, subscriber1!
Received message: Hello, subscriber2!
num: 10

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值