http://blog.csdn.net/soft2967/article/details/8657207
异步:把函数调用封装成一个任务,投递到任务队列里,这样就异步了
什么是回调函数?
回调在不用的语言可以用不同的实现手法,比如C/C++一般使用函数指针,C#这类没有指针的语言可以使用委托,回调的本质上是先用函数指针存储函数的地址,在处理完某个事件/或者满足某个条件的时候调用函数来处理。
2.为什么需要回调了?
在game后台很多处理需要异步的经行,这样才能保证后台程序的高效性。既然需要异步处理,那就是在某个时间点后或者满足某个条件后调用预先指定的函数。这个就叫做回调。
3.如何设计一个通用的回调架构
从需求开始:
假想一个场景需求我们要异步处理对DB的数据处理,比如查询,修改数据都必须是异步的(使用SQL对db的查询一般是同步过程,也就是你select或者insert的时候如果数据没有返回,这时候是不能接着处理下面的语句的,想象下在一个要处理几千玩家的服务器这绝对是不予需的)。那么如何设计了?
接着往下看。
//先的有一个抽象基类,提供一个回调接口run()函数,这样利用多态在调用run的时候,会根据实际的子类对象调用到子类的run
struct runnable
{
virtual void run() = 0;
virtual ~runnable() {};
};
////具体实现 callback.h
- #ifndef __CALLBACK_H_
- #define __CALLBACK_H_
- //#include ""
- struct runnable
- {
- virtual ~runnable() {};
- virtual void run() = 0; //回调接口
- };
- template <typename FUN>
- class CCallFun0 : public runnable
- {
- public:
- CCallFun0(FUN fun) { m_fun = fun; }
- virtual void run()
- {
- m_fun();
- }
- private:
- FUN m_fun;
- };
- template<typename FUN,typename PAR>
- class CCallFun1:public runnable
- {
- public:
- CCallFun1(FUN fun,PAR par)
- :m_fun(fun)
- ,m_par(par)
- {}
- virtual ~CCallFun1() {};
- virtual void run()
- {
- m_fun(m_par);
- }
- private:
- FUN m_fun;
- const PAR m_par;
- };
- template<typename FUN,typename PAR1,typename PAR2>
- class CCallFun2:public runnable
- {
- public:
- CCallFun2(FUN fun,PAR1 par1,PAR2 par2)
- :m_fun(fun),
- m_par1(par1),
- m_par2(par2)
- {}
- virtual void run()
- {
- m_fun(m_par1,m_par2);
- }
- private:
- FUN m_fun;
- const PAR1 m_par1;
- const PAR2 m_par2;
- };
- #endif
////测使用
#include "CallBack.h"
void test(int index)
{
std::cout << index;
}
void strtest(std::string str)
{
std::cout << str;
}
template < typename FUN, typename PARA >
inline runnable * make_fun_runnable(FUN fun, PARA para)
{
return new CCallFun1<FUN, PARA>(fun, para);
}
int _tmain(int argc, _TCHAR* argv[])
{
runnable* runbale = make_fun_runnable(test,5);
runbale->run();
runbale = make_fun_runnable(strtest,"ABCD");
runbale->run();
return 0;
}
根据参数个数不同,可以模仿上面增加类来支持
//那么在DB的查询中如何异步来使用了 大概流程如下
定义回调函数 比如加载玩家信息
//这是一个耗时的操作,把它异步化
void LoadRoleInfoFromDB(int roleid, dataset * data)
{
//dataset为db查询的返回结果,根据实际情况类型会不同
//如mysql的 dataset可能为 ::mysqlpp::StoreQueryResult
//伪代码如下
CPlayer* player = GetPlayer(roleid);
if (player == NULL)
{
return;
}
struct playerinfo info;
//一般会把DB的信息解码成自定义结构体
Encode(data,info);
player->SetInfo(data);
}
//某个Role的请求查询伪代码
//这里是关键,动态
runnable* runbale = make_fun_runnable(LoadRoleInfoFromDB,player->getroleid());
//放入异步队列
SQLQUEUE->PushQueue(SQL,runbale); //push到队列后
////异步处理队列
在处理完毕后
dataset data //查询到的结果
//这个地方需要修改下,让run可以接受参数
//run里就调用LoadRoleInfoFromDB(roleid,data);
runbale->run(data);
当然还可以用在更多场景,
比如在一个定时器里在一个时间间隔内调用回调函数
浙公网安备 33010602011771号