.什么是回调函数?
回调在不用的语言可以用不同的实现手法,比如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);
当然还可以用在更多场景,
比如在一个定时器里在一个时间间隔内调用回调函数
待续