具体事件处理类的基类,实现多态的接口
class iEventHandler
{
public:
virtual iEvent* handle(const iEvent* ev) { return NULL; };
virtual ~iEventHandler() {};
iEventHandler(const char* name) :name_(name) {};
std::string& get_name() { return name_; };
private:
std::string name_;
};
根据事件类型调用不同的函数进行处理
// 创建完成之后先去构造函数中把他们订阅
class UserEventHandler : public iEventHandler
{
public:
UserEventHandler();
virtual ~UserEventHandler();
virtual iEvent* handle(const iEvent* ev);
private:
// 在handle内调用
MobileCodeRspEv* handle_mobile_code_req(MobileCodeReqEv* ev); // 验证码请求
LoginResEv* handle_login_req(LoginReqEv* ev); // 登录请求
i32 code_gen(); // 生成验证码,实际中这个功能是由运营商提供的接口来实现的
private:
//std::string mobile_; // phone number
std::map<std::string, i32> m2c_; //first is mobile, second is code <mobile, icode>
pthread_mutex_t pm_;
};
构造函数中订阅,析构函数里解除绑定:
UserEventHandler::UserEventHandler() :iEventHandler("UserEventHandler")
{
//构造函数实现订阅事件的处理
DispatchMsgService::getInstance()->subscribe(EEVENTID_GET_MOBLIE_CODE_REQ, this);
DispatchMsgService::getInstance()->subscribe(EEVENTID_LOGIN_REQ, this);
thread_mutex_create(&pm_);
}
UserEventHandler::~UserEventHandler()
{
// 析构函数实现退订事件的处理
DispatchMsgService::getInstance()->unsubscribe(EEVENTID_GET_MOBLIE_CODE_REQ, this);
DispatchMsgService::getInstance()->unsubscribe(EEVENTID_LOGIN_REQ, this);
thread_mutex_destroy(&pm_);
}
根据事件类型调用不同的函数进行处理:
iEvent* UserEventHandler::handle(const iEvent* ev)
{
if (ev == NULL)
{
LOG_ERROR("input ev is NULL");
//printf("input ev is NULL");
}
u32 eid = ev->get_eid();
if (eid == EEVENTID_GET_MOBLIE_CODE_REQ)
{
return handle_mobile_code_req((MobileCodeReqEv*)ev);
}
else if (eid == EEVENTID_LOGIN_REQ)
{
return handle_login_req((LoginReqEv*) ev);
}
else if (eid == EEVENTID_RECHARGE_REQ)
{
//return handle_recharge_req((RechargeEv*) ev);
}
else if (eid == EEVENTID_GET_ACCOUNT_BALANCE_REQ)
{
//return handle_get_account_balance_req((GetAccountBalanceEv*) ev);
}
else if (eid == EEVENTID_LIST_ACCOUNT_RECORDS_REQ)
{
//return handle_list_account_records_req((ListAccountRecordsReqEv*) ev);
}
return NULL;
}
处理手机验证码请求:
//短信验证码请求处理函数,接收短信验证码请求类,处理并返回短信验证码响应类
MobileCodeRspEv* UserEventHandler::handle_mobile_code_req(MobileCodeReqEv* ev)
{
i32 icode = 0;
std::string mobile_ = ev->get_mobile();
LOG_DEBUG("try to get moblie phone %s validate code .", mobile_.c_str());
printf("try to get moblie phone %s validate code .\n", mobile_.c_str());
icode = code_gen();
thread_mutex_lock(&pm_); //被线程池调用,锁住临界资源
m2c_[mobile_] = icode;
thread_mutex_unlock(&pm_);
LOG_DEBUG("mobile: %s, code: %d\n", mobile_.c_str(), icode);
printf("mobile: %s, code: %d\n", mobile_.c_str(), icode);
return new MobileCodeRspEv(ERRC_SUCCESS, icode);
}
相关知识
完整的线程池由两个部分组成,分别是存放任务的任务队列,用来放置需要处理的任务,以及处理任务的线程池,由多个线程组成,用来处理任务。
在线程池创建后,启动多个线程,绑定同一个处理函数,该处理函数的功能就是循环提取任务队列中的任务进行处理。在提取任务时,需要对任务队列进行操作,但因为任务队列是共享资源,所以线程在操作它时,需要进行线程间的同步互斥。这一点通过互斥锁与条件变量完成。
多线程的好处:
1.使用线程可以把占据时间长的程序中的任务放到后台去处理。
2.用户界面更加吸引人,这样比如用户点击了一个按钮去触发某件事件的处理,可以弹出一个进度条来显示处理的进度。
3.程序的运行效率可能会提高。
4.在一些等待的任务实现上如用户输入,文件读取和网络收发数据等,线程就比较有用了。
多线程的缺点:
1.如果有大量的线程,会影响性能,因为操作系统需要在它们之间切换。
2.更多的线程需要更多的内存空间。
3.线程中止需要考虑对程序运行的影响。
4.通常块模型数据是在多个线程间共享的,需要防止线程死锁情况的发生。
pthread_mutex_lock 函数是一个线程同步函数,用于对互斥锁进行加锁操作。它阻塞调用线程,直到可以获得互斥锁为止。如果互斥锁已经被其他线程锁定,则调用线程将被阻塞,直到互斥锁被解锁。
如果 mutex 指向的互斥锁当前没有被锁定,则调用线程将获得锁,并继续执行。如果 mutex 指向的互斥锁已经被其他线程锁定,则调用线程将被阻塞,直到互斥锁被解锁。
互斥锁(Mutex):
把共享资源包裹起来,确保任何时候只有一个线程可以访问该资源
递归锁(Recursive Mutex):
递归锁是一种特殊的互斥锁,它允许同一线程多次对同一个锁进行锁定操作。递归锁可以用std::recursive_mutex进行实现。
独占锁(Unique Lock):
独占锁是一种智能锁,它允许程序员在控制块作用域内自动获取和释放锁。独占锁可以用std::unique_lock进行实现。
条件变量(Conditional Variable):
条件变量是一种允许线程等待特定条件的机制,直到满足某些条件才允许线程在访问共享资源之前进行访问。条件变量可以用std::condition_variable进行实现。
避免忙等待(短时间等待自旋锁,长时间等待条件变量):与忙等待(busy-waiting)相比,条件变量允许线程进入休眠状态,只有在条件满足时才会被唤醒。 避免竞争条件:条件变量通常与互斥锁(mutex)一起使用,以确保在检查条件和等待条件之间的操作是原子的。这有助于防止竞争条件(race condition)和数据竞争(data race)。
读写锁(Reader-Writer Lock):
读写锁是一种可以同时支持多个读操作和单个写操作的锁,它可以提高并发性能。读写锁可以用std::shared_timed_mutex进行实现。