C++服务开发入门指南
  • 序言
  • 前言
  • 一、一个简单的服务
    • 1 什么是服务
    • 2 服务可以用来做什么
    • 3 简单服务框架
  • 二、网络通信服务框架
    • 1 网络服务的基本概念
    • 2 增加监听端口
    • 3 处理客户端会话
    • 小结
  • 三、添加基础模块
    • 1 日志模块
    • 2 定时器
    • 3 事件机制
    • 4 线程池
    • 5 线程安全
    • 小结
  • 四、一个聊天服务
    • 1 需求描述及分析
    • 2 概要设计
    • 3 创建服务项目
    • 4 ClientUser实现
    • 5 RoomMgr实现
    • 6 ChatRoom实现
    • 7 RoomIDMgr实现
    • 小结
  • 五、测试、迭代及重构
    • 1 测试
    • 2 迭代
    • 3 重构
    • 4 版本号
  • 六、架构设计
    • 1 单点服务
    • 2 分布式服务
  • 七、部署及发布
    • 1 部署环境
    • 2 编译环境
    • 3 部署服务
    • 4 发布服务
  • 八、线上问题处理
    • 1 线上问题
    • 2 问题处理
  • 九、程序员的职业规划
    • 职业规划
Powered by GitBook
On this page
  • 1、什么是事件
  • 2、事件机制有什么用
  • 3、事件机制实现
  • 4、调用方式
  1. 三、添加基础模块

3 事件机制

Previous2 定时器Next4 线程池

Last updated 2 years ago

1、什么是事件

首先,需要明确下什么叫事件。

事件,对应的英文是Event,表示有一件事情发生了。

如果一件事情,不与其他的对象相关联,发生时我们只要直接调用函数就可以了。就像上一节定时器实现一样,我们检测到定时器到期了,直接执行定时器的回调函数。

但如果涉及到两个对象,触发事件的一方,就需要一定的手段通知对方。

比如,你去朋友公司拜访他,朋友告诉你,你到公司门口,给我打电话,我下去接你。

这就是一个典型的事件场景:你到朋友公司门口,触发事件,打电话通知朋友,朋友得知你到公司门口,下楼来接你。

这个事件的通知机制,是你有朋友的电话号码,可以通过电话直接通知朋友。

那如果老板通知各部门经理,下午三点开会呢?

你应该已经想到了,直接在群里发个消息就行了,不用每个人打一个电话。

2、事件机制有什么用

上述的事件,虽然能够通知到相关角色,但通知发布的人,需要知道被通知人的联系方式,这样在程序开发中耦合太紧密了。

有时候,事件触发的对象,并不知道谁需要对事件做出响应,比如公司发出公告:下午2:00公司做核酸检测,今天没有做过核酸的同学到一楼大厅做核酸。

这个事件中,哪些人需要响应事件是需要结合自身情况处理的。而发布者是不关心具体谁要去执行,只需要将事件通知出去。

我们要实现的事件机制,就是用来解耦的。

我们可以通过一个发布订阅机制来实现事件通知。

  1. 首先有一个事件中心,各个对象实例可以到事件中心,注册自己关心的事件ID。

  2. 事件触发的模块,可以到事件中心发布事件,通知事件已经触发。

  3. 事件中心检索,是否有实例关心这个事件,如果有则通知相关实例。

这样,发布者就不需要了解谁关心这个事件,只需要将事件发布出去即可,由事件中心负责通知关心的实例。这样就将事件发布者和事件响应者解耦了。

3、事件机制实现

事件机制实现与上一节的定时器实现很相似,这里直接贴上代码分析

#pragma once
#include <functional>
#include <unordered_map>
#include <list>
#include <mutex>

class CHYEvent;
typedef std::function<void(CHYEvent*)> EventCBFunc;

typedef std::mutex Mutex;
typedef std::lock_guard<Mutex> AutoLock;

class CHYEvent
{
public:
 bool Copy(CHYEvent& event){
        nType = event.nType;
        nDataLen = event.nDataLen;
        if (nDataLen != 0)
        {
            memcpy(this+1, &event+1, nDataLen);
        }
        return true;
    }

public:
    int nType = 0;
    int nDataLen = 0; 
};

class EventMgr
{
private:
    EventMgr();

public:
    static EventMgr* Instance();

public:
    bool RegisterEvent(int nEventType, void* owner, EventCBFunc func);

    bool UnregisterEvent(int nEventType, void* owner);

    bool PostEvent(CHYEvent* pEvent);

    bool OnEvent(CHYEvent* pEvent);

private:
    unsigned long	WorkThreadFunc();

    std::unordered_map<int, std::unordered_map<void*, EventCBFunc>> m_mapEvent;
    Mutex                        m_lockEventMap;

    std::list<CHYEvent*>         m_listEvent;
    Mutex                        m_lockEvent;
    std::condition_variable      m_cond;
};

Event只需定义两个变量,一个是Type表示事件类型,一个是DataLen记录后缀数据长度,用于Event类型扩展。

EventMgr有两个重要变量,m_mapEvent用来记录事件注册信息,收到事件后会在这个map中查询需要调用哪些回调。

m_listEvent记录当前的Event缓存队列,虽然Event都是立即处理的,但在多线程中,仍然可能出现处理不及时而积压的情况,因此需要一个容器存放缓存队列。

EventMgr启动一个工作线程(WorkThreadFunc),查询m_listEvent是否有数据,没有数据就等待,线程挂起。这里与定时器不同,定时器需要sleep一个间隔后再次运行,用于校验是否到期。

事件触发者可以调用PostEvent,向m_listEvent插入一个Event,并通过条件变量m_cond通知工作线程,事件队列中有数据了,需要工作线程进行处理。

工作线程取出事件,交给OnEvent函数处理,在m_mapEvent中查找回调函数并回调。

#include "HYEvent.h"
#include <thread>

EventMgr::EventMgr()
{
    std::thread th(&EventMgr::WorkThreadFunc, this);
	th.detach();
}

EventMgr* EventMgr::Instance()
{
    static EventMgr instance;
    return &instance;
}

bool EventMgr::RegisterEvent(int nEventType, void* owner, EventCBFunc func)
{
    AutoLock lock(m_lockEventMap);
    if ( m_mapEvent.count(nEventType) == 0 )
    {
        std::unordered_map<void*, EventCBFunc> map;
        map[owner] = func;
        m_mapEvent[nEventType] = map;
    }
    else
    {
        m_mapEvent[nEventType][owner] = func;
    }
    
    return true;    
}

bool EventMgr::UnregisterEvent(int nEventType, void* owner)
{
    AutoLock lock(m_lockEventMap);
    if ( m_mapEvent.count(nEventType) == 0 ) return false;

    if (m_mapEvent[nEventType].count(owner) == 0) return false;
    
    m_mapEvent[nEventType].erase(owner);

    if (m_mapEvent[nEventType].size() == 0)
    {
        m_mapEvent.erase(nEventType);
    }
    
    return true;
}

bool EventMgr::PostEvent(CHYEvent* pEvent)
{
    int nSize = sizeof(CHYEvent) + pEvent->nDataLen;
    char* t = new char[nSize];
    CHYEvent* event = (CHYEvent*)t;
    event->Copy(*pEvent);

    std::unique_lock<std::mutex> locker(m_lockEvent);
    m_listEvent.push_back(event);
    locker.unlock();
    m_cond.notify_one();

    return true;
}

 unsigned long EventMgr::WorkThreadFunc()
 {
    while(true)
    {
        std::unique_lock<std::mutex> locker(m_lockEvent);
        while (m_listEvent.empty())
            m_cond.wait(locker);
        
        CHYEvent* pEvent = m_listEvent.front();
        m_listEvent.pop_front();
        locker.unlock();

        OnEvent(pEvent);
    }
    return 0;
 }

 bool EventMgr::OnEvent(CHYEvent* pEvent)
 {
    AutoLock lock(m_lockEventMap);
    if ( m_mapEvent.count(pEvent->nType) == 0 ) return false;

    auto & map = m_mapEvent[pEvent->nType];
    for (auto itr : map)
    {
        itr.second(pEvent);
    }
    char*e = (char*)pEvent;
    delete []e;
    return true;
 }

4、调用方式

我们在ServiceBase中封装调用,方便派生服务使用

public:
	virtual void PostEvent(CHYEvent* pEvent);

	virtual void OnEvent(CHYEvent* pEvent);
void CServiceBase::PostEvent(CHYEvent* pEvent)
{
	EventMgr::Instance()->PostEvent(pEvent);
}

void CServiceBase::OnEvent(CHYEvent* pEvent)
{
	switch (pEvent->nType)
	{
	case 2:
		LogInfo("this is Service event=s " );
		break;
	
	default:
		break;
	}
}

// 测试代码
	EventMgr::Instance()->RegisterEvent(2, this, [&](CHYEvent* event){
		LogInfo("Service On event %d", event->nType);
		OnEvent(event);
	});

	CHYEvent event;
	event.nType = 2;
	EventMgr::Instance()->PostEvent(&event);

基类注册了监听事件(2),由基类的OnEvent函数处理。此后,在服务的任意位置触发这个事件,服务基类都可以收到事件信息,并进行处理。

为管理方便,需要有一个统一的头文件定义事件ID。