C++:跨模块调用服务

跨模块调用是一个比较头疼的问题,往往因为模块耦合性比较强,使得代码逻辑比较复杂。

 

1. 如何能实现跨模块调用,又不互相耦合?

通知者不关心谁需要处理该消息,处理者也不一定需要知道是谁发送了此消息。

windows自带的消息机制刚好可以解决该问题,但是此消息机制有几个致命的缺点:一,必须有窗口支持,当然该部分逻辑可以注册一个虚拟窗口来处理,但是也为编码带来了很大阻碍。二,更致命的是,消息队列在资源紧缺的情况下,还会出现消息丢失的情况。

但是PostMessage这个思维还是值得推广的。

 

2. 参数如何传递?

传统思想就是通过传递void*来传递,但是这个办法有个两个比较恶心的地方:一,类型不安全。   二,需要自己手动释放该指针。C++17引入了any,完美解决了以上两个问题,也为泛型编程提供了很多便利。 

 

以下就是代码实现的思路:

1. MessageService提供了注册,以及PostMessage功能,是业务功能的核心支持。

2. IMsgBus是所有接收者的基类,接收者需要派生该类来实现消息接收。

3. 考虑到有某些情况,发送者发送消息时,需要通知的模块可能还未初始化,使得接收可能会丢失这条消息。针对该情况可以在MessageService里面提供一个缓存功能。(该功能还未实现)

 

下面贴上代码实现:

#include <windows.h>
#include <iostream>
#include <any>
#include <functional>
#include <string>
#include <mutex>
#include <queue>

class IMsgBus
{
public:
    virtual ~IMsgBus() {}
    virtual void Recv(std::any) = 0;
};

using MsgBusPtr = std::shared_ptr<IMsgBus>;

class MessageService
{
public:
    ~MessageService() 
    {
        _message_loop.detach();
        _stop = true;
        for (auto recv : _queue_recv)
        {
            recv.second.clear();
        }
        _queue_recv.clear();
        _queue_send.empty();
    }

    static MessageService& Instance()
    {
        static std::once_flag s_flag;
        std::call_once(s_flag, [&]() {
            _instance.reset(new MessageService);
            });
        return *_instance;
    }

    void PostMessage(std::any message) 
    {
        std::unique_lock<std::mutex> lock(_lock_send);
        _queue_send.push(message);
        _var_send.notify_one();
    }

    void Register(std::any message, MsgBusPtr recv)
    {
        std::unique_lock<std::mutex> lock(_lock_recv);
        _queue_recv[message.type().name()].push_back(recv);
    }

    void Start()
    {
        _stop = false;

        _message_loop = std::thread(std::bind(&MessageService::MessageLoop, this));

        //task.join();
    }

    void Stop()
    {
        _stop = true;
    }

private:
    MessageService() {}
    void MessageLoop()
    {
        while (!_stop)
        {
            std::any message;

            if (1)
            {
                std::unique_lock<std::mutex> lock(_lock_send);
                if (0 == _queue_send.size()) _var_send.wait(lock);
                //if (0 == _queue_send.size())continue;

                message = _queue_send.front();
                _queue_send.pop();
            }
            
            std::unique_lock<std::mutex> lock(_lock_recv);
            auto type = message.type().name();
            if (_queue_recv.end() != _queue_recv.find(type))
            {
                for (auto recv : _queue_recv[type])
                {
                    std::invoke(std::bind(&IMsgBus::Recv,recv,std::placeholders::_1),message);
                }
                _queue_recv[type].clear();
            }
        }
    }

    std::mutex _lock_recv;
    std::mutex _lock_send;
    std::condition_variable _var_send;
    volatile bool _stop = true;

    std::queue<std::any> _queue_send;
    std::unordered_map<std::string, std::vector<MsgBusPtr>> _queue_recv;

    std::thread _message_loop;
    
    static std::unique_ptr<MessageService> _instance;
};

std::unique_ptr<MessageService> MessageService::_instance;

struct TestMessage
{
    std::string msg;
    int errid;
};

class MsgBus :public IMsgBus
{
public:
    MsgBus() {}

    void Recv(std::any message)
    {
        if (typeid(TestMessage).name() == message.type().name())
        {
            auto msg = std::any_cast<TestMessage>(message);
            std::cout << "11111msg:" << msg.msg << "msgid:" << msg.errid << std::endl;
            Sleep(2000);
        }
    }
};

class MsgBus2 :public IMsgBus
{
public:
    MsgBus2() {}

    void Recv(std::any message)
    {
        if (typeid(TestMessage).name() == message.type().name())
        {
            auto msg = std::any_cast<TestMessage>(message);
            std::cout << "22222msg:" << msg.msg << "msgid:" << msg.errid << std::endl;
        }
    }
};

int main()
{
    MessageService::Instance().Start();
    TestMessage msg = { "222",10 };
    std::any message = msg;
    MsgBusPtr ptr = std::shared_ptr<IMsgBus>(new MsgBus());
    MsgBusPtr ptr2 = std::shared_ptr<IMsgBus>(new MsgBus2());
    MessageService::Instance().Register(TestMessage(), ptr);
    MessageService::Instance().Register(TestMessage(), ptr2);

    MessageService::Instance().PostMessage(message);

    std::cin.get();
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值