博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

游戏后台里的通用技术之异步处理和回调

Posted on 2016-04-14 17:21  bw_0927  阅读(523)  评论(0)    收藏  举报

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

 

[cpp] view plain copy
 
  1. #ifndef __CALLBACK_H_  
  2. #define __CALLBACK_H_  
  3. //#include ""  
  4.   
  5. struct runnable  
  6. {  
  7.   virtual ~runnable() {};  
  8.   virtual void run() = 0;  //回调接口  
  9. };  
  10.   
  11.   
  12. template <typename FUN>  
  13. class CCallFun0 : public runnable  
  14. {  
  15. public:  
  16.     CCallFun0(FUN fun) { m_fun = fun; }  
  17.     virtual void run()   
  18.     {  
  19.         m_fun();  
  20.     }  
  21. private:  
  22.     FUN m_fun;  
  23. };  
  24.   
  25. template<typename FUN,typename PAR>  
  26. class CCallFun1:public runnable  
  27. {  
  28. public:  
  29.     CCallFun1(FUN fun,PAR par)  
  30.         :m_fun(fun)   
  31.         ,m_par(par)  
  32.     {}  
  33.     virtual ~CCallFun1() {};  
  34.     virtual void run()   
  35.     {  
  36.         m_fun(m_par);  
  37.     }  
  38. private:  
  39.     FUN  m_fun;  
  40.     const PAR  m_par;  
  41. };  
  42.   
  43. template<typename FUN,typename PAR1,typename PAR2>  
  44. class CCallFun2:public runnable  
  45. {  
  46. public:  
  47.     CCallFun2(FUN fun,PAR1 par1,PAR2 par2)  
  48.         :m_fun(fun),  
  49.         m_par1(par1),  
  50.         m_par2(par2)  
  51.     {}  
  52.       
  53.     virtual void run()   
  54.     {  
  55.         m_fun(m_par1,m_par2);  
  56.     }  
  57.   
  58. private:  
  59.     FUN m_fun;  
  60.     const PAR1 m_par1;  
  61.     const PAR2 m_par2;  
  62. };  
  63.   
  64. #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); 

 

当然还可以用在更多场景,

比如在一个定时器里在一个时间间隔内调用回调函数