用户态线程在AI中的应用

本文来自sniperhuangwei的专栏,此处纯粹收藏,转载请标明作者及原文出处,以示尊重!!

原文作者:sniperhuangwei

原文出处:http://blog.youkuaiyun.com/sniperhuangwei/archive/2010/07/14/5735610.aspx

最近这段时间在修改服务器AI,准备将AI分配到单独的服务器中做,但为了不至于对原有架构造成

太大的影响,攻击的判定,移动的判定仍然在gameserver上处理,AI服务器的角色就是根据状态

选择合适的决策并向gameserver发出决策命令。

例如:一个简单的AI函数可能像下面这样

view plaincopy to clipboardprint?
void onAi()  
{  
  //从视野中选择一个目标  
  target = findtarget();  
  if(target && target.distence(this) <= 10)  
  {  
      attack(target);  
  }  

void onAi()
{
  //从视野中选择一个目标
  target = findtarget();
  if(target && target.distence(this) <= 10)
  {
      attack(target);
  }
}
 

调用attack的时候,将向gameserver发送一条攻击命令,由gameserver做出判定并将结果返回

给Ai服务器。这时就出现了一个问题,如果Ai循环又一次执行到onAi,但前一次的攻击结果还没有返回。

这个时候ai就不能根据正确的状态做出合理的决策了。

正确的做法是执行attack的时候阻塞在attack上,直到攻击结果返回attack才返回。

显然,如果阻塞在attack上,那么将导致其它AI无法继续运行,这个时候,用户态线程的威力就发挥出来

了。

执行attack的时候,可以把路径切换回调度器,由调度器选择没有被阻塞的用户态线程序来执行,当攻击

结果返回后,把被阻塞的用户态线程重新置为可运行状态,调度器以后可以重新调度该线程继续执行。

当调度器重新调度被阻塞的线程时,那个线程将会从attack中返回,继续后续的处理。这样保证了AI

的串行处理。

用户态线程的实现有很多种方式,包括linux的ucontext和windows下的fiber,下面给出一个fiber实现的

简单的用户态线程调度框架.

uthread.h

view plaincopy to clipboardprint?
#ifndef _UTHREAD_H  
#define _UTHREAD_H  
 
#include <Windows.h>  
 
enum 
{  
    NONE,           //仅仅让出处理器  
    WAIT4EVENT = 1, //等待某事件的来临  
    WAIT4EVENTTIMEOUT,  
    DEAD,           //纤程已死亡  
    ACTIVED,        //可运行的  
    SLEEP,  
};  
 
//纤程等待的事件  
enum 
{  
    MOVE_RET = 1,   //移动的返回结果  
    SKILL_RET,      //技能使用的返回结果  
};  
 
typedef int uthread_t;  
 
 
class uthread;   
class runnable  
{  
public:  
    virtual void main_routine() = 0;  
    uthread *p_uthread;  
};  
 
//纤程  
class uthread  
{  
public:  
 
    static void WINAPI thread_routine(LPVOID pvParam);  
      
    void OnEvent(unsigned short ev);  
      
    uthread_t uthread_id;  
    unsigned char status;  
    PVOID p_uthreadContext;  
 
    unsigned short waitevent;//一个纤程只能等待在一个事件上  
 
};  
 
 
#endif 
#ifndef _UTHREAD_H
#define _UTHREAD_H

#include <Windows.h>

enum
{
 NONE,           //仅仅让出处理器
 WAIT4EVENT = 1, //等待某事件的来临
 WAIT4EVENTTIMEOUT,
 DEAD,           //纤程已死亡
 ACTIVED,        //可运行的
 SLEEP,
};

//纤程等待的事件
enum
{
 MOVE_RET = 1,   //移动的返回结果
 SKILL_RET,      //技能使用的返回结果
};

typedef int uthread_t;


class uthread;
class runnable
{
public:
 virtual void main_routine() = 0;
 uthread *p_uthread;
};

//纤程
class uthread
{
public:

 static void WINAPI thread_routine(LPVOID pvParam);
 
 void OnEvent(unsigned short ev);
 
 uthread_t uthread_id;
 unsigned char status;
 PVOID p_uthreadContext;

 unsigned short waitevent;//一个纤程只能等待在一个事件上

};


#endif

Scheduler.h

view plaincopy to clipboardprint?
#ifndef _SCHEDULER_H  
#define _SCHEDULER_H  
#include <Windows.h>  
 
#include "uthread.h"  
#include <map>  
#include <list>  
#include <time.h>  
 
#define MAX_FIBER 8192  
 
class Scheduler  
{  
friend class uthread;  
public:  
    //初始化纤程库  
    static void scheduler_init()  
    {  
         m_pUthreadContext = ConvertThreadToFiber(NULL);  
    }  
 
    static void scheduler_destroy();  
 
    static uthread_t spawn(runnable *param,int stacksize);  
 
    //选择一个纤程以进行调度  
    static void schedule();  
 
    static void sleep(time_t timeout)  
    {  
        if(timeout > 0)  
        {  
            m_uthreads[m_curuid]->status = SLEEP;  
            time_t t = timeout + time(NULL);  
            m_sleepList.push_back(std::make_pair(t,m_uthreads[m_curuid]));  
        }  
 
        SwitchToFiber(m_pUthreadContext);  
 
    }  
 
    //将一个纤程添加到可运行队列中  
    static void add2Active(uthread *ut)  
    {  
        ut->status = ACTIVED;  
        m_pendingAdd.push_back(ut);  
    }  
 
    //将当运行权交给scheduler  
    static void yield()  
    {  
        //将运行权交给调度器  
        SwitchToFiber(m_pUthreadContext);  
    }  
 
    //阻塞在ev上,timeout==0将永远等待  
    static int block(unsigned short ev,time_t timeout);  
 
private:  
 
    static std::map<PVOID,uthread*> m_activeList;//可运行列表  
 
    static std::list<uthread*> m_pendingAdd;//等待添加进可运行列表中的纤程  
 
    static std::list<std::pair<time_t,uthread*> > m_sleepList;//正在睡眠的纤程,将来改成用优先队列  
 
    static PVOID m_pUthreadContext;//调度器所在纤程的上下文  
      
    static uthread *m_uthreads[MAX_FIBER];  
 
    static int m_count;  
 
    static int m_curuid;           //当前正在运行的纤程的uid,==-1表示在scheduler中运行  
 
    static volatile bool m_terminate;         
 
};  
 
 
 
 
#endif 
#ifndef _SCHEDULER_H
#define _SCHEDULER_H
#include <Windows.h>

#include "uthread.h"
#include <map>
#include <list>
#include <time.h>

#define MAX_FIBER 8192

class Scheduler
{
friend class uthread;
public:
 //初始化纤程库
 static void scheduler_init()
 {
   m_pUthreadContext = ConvertThreadToFiber(NULL);
 }

 static void scheduler_destroy();

 static uthread_t spawn(runnable *param,int stacksize);

 //选择一个纤程以进行调度
 static void schedule();

 static void sleep(time_t timeout)
 {
  if(timeout > 0)
  {
   m_uthreads[m_curuid]->status = SLEEP;
   time_t t = timeout + time(NULL);
   m_sleepList.push_back(std::make_pair(t,m_uthreads[m_curuid]));
  }

  SwitchToFiber(m_pUthreadContext);

 }

 //将一个纤程添加到可运行队列中
 static void add2Active(uthread *ut)
 {
  ut->status = ACTIVED;
  m_pendingAdd.push_back(ut);
 }

 //将当运行权交给scheduler
 static void yield()
 {
  //将运行权交给调度器
  SwitchToFiber(m_pUthreadContext);
 }

 //阻塞在ev上,timeout==0将永远等待
 static int block(unsigned short ev,time_t timeout);

private:

 static std::map<PVOID,uthread*> m_activeList;//可运行列表

 static std::list<uthread*> m_pendingAdd;//等待添加进可运行列表中的纤程

 static std::list<std::pair<time_t,uthread*> > m_sleepList;//正在睡眠的纤程,将来改成用优先队列

 static PVOID m_pUthreadContext;//调度器所在纤程的上下文
 
 static uthread *m_uthreads[MAX_FIBER];

 static int m_count;

 static int m_curuid;           //当前正在运行的纤程的uid,==-1表示在scheduler中运行

 static volatile bool m_terminate;      

};

 


#endif

Scheduler.cpp

view plaincopy to clipboardprint?
#include "stdafx.h"  
#include "Scheduler.h"  
 
std::map<PVOID,uthread*> Scheduler::m_activeList;//可运行列表  
 
std::list<uthread*> Scheduler::m_pendingAdd;  
 
std::list<std::pair<time_t,uthread*> > Scheduler::m_sleepList;  
 
 
PVOID Scheduler::m_pUthreadContext;//调度器所在纤程的上下文  
 
uthread *Scheduler::m_uthreads[MAX_FIBER];  
 
int Scheduler::m_count = 0;  
 
int Scheduler::m_curuid = -1;  
 
volatile bool Scheduler::m_terminate = false;  
 
 
void WINAPI uthread::thread_routine(LPVOID pvParam)  
{  
    ((runnable*)pvParam)->main_routine();  
    ((runnable*)pvParam)->p_uthread->status = DEAD;  
    /*这里不能直接退出纤程运行函数,否则会导致运行线程的退出, 
    * 正确的做法是把运行权交回给scheduler,由scheduler来删除 
    * 这个纤程 
    */ 
    Scheduler::yield();  
}  
 
//等待的事件到达了,将纤程重新插入到可运行队列中  
void uthread::OnEvent(unsigned short ev)  
{  
    if(ev == waitevent)  
    {  
        status = ACTIVED;  
        Scheduler::add2Active(this);  
        waitevent = 0;  
 
        //从sleeplist中删除  
        std::list<std::pair<time_t,uthread*> >::iterator it = Scheduler::m_sleepList.begin();  
        std::list<std::pair<time_t,uthread*> >::iterator end = Scheduler::m_sleepList.end();  
        for( ; it != end; ++it )  
        {  
 
            if(it->second == this)  
            {  
                it = Scheduler::m_sleepList.erase(it);  
                break;  
            }  
        }  
    }  
}  
 
void Scheduler::schedule()  
{  
    printf("schedule/n");  
    while(!m_terminate)  
    {  
        std::list<std::map<PVOID,uthread*>::iterator> deletes;  
 
        std::map<PVOID,uthread*>::iterator it = m_activeList.begin();  
        std::map<PVOID,uthread*>::iterator end = m_activeList.end();  
        for( ; it != end; ++it)  
        {  
            m_curuid = it->second->uthread_id;  
            SwitchToFiber(it->first);  
            m_curuid = -1;  
            if(it->second->status == DEAD || it->second->status == SLEEP || it->second->status == WAIT4EVENT  
                || it->second->status == WAIT4EVENTTIMEOUT)  
            {  
                deletes.push_back(it);  
            }  
            printf("come back/n");  
        }  
 
        {  
            std::list<std::map<PVOID,uthread*>::iterator>::iterator it = deletes.begin();  
            std::list<std::map<PVOID,uthread*>::iterator>::iterator end = deletes.end();  
            for( ; it != end; ++it)  
            {  
                if((*it)->second->status == DEAD)  
                {  
                    DeleteFiber((*it)->first);  
                    m_uthreads[(*it)->second->uthread_id] = NULL;  
                    delete (*it)->second;  
                    --m_count;  
                }  
                m_activeList.erase(*it);  
            }  
        }  
        //将所有等待添加到m_activeList中的纤程都添加进去  
        {  
            while(!m_pendingAdd.empty())  
            {  
                uthread *tmp = m_pendingAdd.back();  
                m_pendingAdd.pop_back();  
                m_activeList.insert(std::make_pair(tmp->p_uthreadContext,tmp));  
            }  
                  
        }  
              
        //看看有没有timeout的纤程  
        {  
            time_t now = time(NULL);  
            std::list<std::pair<time_t,uthread*> >::iterator it = m_sleepList.begin();  
            for( ; it != m_sleepList.end(); )  
            {  
                time_t t = it->first;  
                if(it->first <= now)  
                {  
                    it->second->status = ACTIVED;   
                    m_activeList.insert(std::make_pair(it->second->p_uthreadContext,it->second));  
                    it = m_sleepList.erase(it);  
                }  
                else 
                    ++it;  
            }  
        }  
 
    }  
 
    scheduler_destroy();  
    ConvertFiberToThread();  
    printf("scheduler end/n");  
}  
 
void Scheduler::scheduler_destroy()  
{  
    for(int i = 0; i < MAX_FIBER; ++i)  
    {  
        if(m_uthreads[i])  
        {  
            DeleteFiber(m_uthreads[i]->p_uthreadContext);  
            delete m_uthreads[i];  
        }  
    }  
}  
 
uthread_t Scheduler::spawn(runnable *param,int stacksize)//创建一个新的纤程   
{  
    if(m_count >= MAX_FIBER)  
        return -1;  
    //刚创建的纤程不处于可运行状态  
    PVOID uthreadcontext =  CreateFiber(stacksize,uthread::thread_routine,param);  
    uthread *nthread = new uthread;  
    nthread->p_uthreadContext = uthreadcontext;  
    for(int i= 0; i < MAX_FIBER; ++i)  
    {  
        if(0 == m_uthreads[i])  
        {  
            nthread->uthread_id = i;  
            m_uthreads[i] = nthread;  
            break;  
        }  
    }  
    add2Active(nthread);  
    ++m_count;  
    param->p_uthread = nthread;  
    return nthread->uthread_id;  
}  
 
static int Scheduler::block(unsigned short ev,time_t timeout)  
{  
    m_uthreads[m_curuid]->waitevent = ev;  
    if(timeout > 0)  
    {  
        m_uthreads[m_curuid]->status = WAIT4EVENTTIMEOUT;  
        time_t t = timeout + time(NULL);  
        m_sleepList.push_back(std::make_pair(t,m_uthreads[m_curuid]));  
    }  
    else 
        m_uthreads[m_curuid]->status = WAIT4EVENT;  
 
    SwitchToFiber(m_pUthreadContext);  
 
    if(m_uthreads[m_curuid]->waitevent == 0)  
    {  
        //等待的事件到达  
        return 0;  
    }  
    else 
        return -1;//超时间  
 

#include "stdafx.h"
#include "Scheduler.h"

std::map<PVOID,uthread*> Scheduler::m_activeList;//可运行列表

std::list<uthread*> Scheduler::m_pendingAdd;

std::list<std::pair<time_t,uthread*> > Scheduler::m_sleepList;


PVOID Scheduler::m_pUthreadContext;//调度器所在纤程的上下文

uthread *Scheduler::m_uthreads[MAX_FIBER];

int Scheduler::m_count = 0;

int Scheduler::m_curuid = -1;

volatile bool Scheduler::m_terminate = false;


void WINAPI uthread::thread_routine(LPVOID pvParam)
{
 ((runnable*)pvParam)->main_routine();
 ((runnable*)pvParam)->p_uthread->status = DEAD;
 /*这里不能直接退出纤程运行函数,否则会导致运行线程的退出,
 * 正确的做法是把运行权交回给scheduler,由scheduler来删除
 * 这个纤程
 */
 Scheduler::yield();
}

//等待的事件到达了,将纤程重新插入到可运行队列中
void uthread::OnEvent(unsigned short ev)
{
 if(ev == waitevent)
 {
  status = ACTIVED;
  Scheduler::add2Active(this);
  waitevent = 0;

  //从sleeplist中删除
  std::list<std::pair<time_t,uthread*> >::iterator it = Scheduler::m_sleepList.begin();
  std::list<std::pair<time_t,uthread*> >::iterator end = Scheduler::m_sleepList.end();
  for( ; it != end; ++it )
  {

   if(it->second == this)
   {
    it = Scheduler::m_sleepList.erase(it);
    break;
   }
  }
 }
}

void Scheduler::schedule()
{
 printf("schedule/n");
 while(!m_terminate)
 {
  std::list<std::map<PVOID,uthread*>::iterator> deletes;

  std::map<PVOID,uthread*>::iterator it = m_activeList.begin();
  std::map<PVOID,uthread*>::iterator end = m_activeList.end();
  for( ; it != end; ++it)
  {
   m_curuid = it->second->uthread_id;
   SwitchToFiber(it->first);
   m_curuid = -1;
   if(it->second->status == DEAD || it->second->status == SLEEP || it->second->status == WAIT4EVENT
    || it->second->status == WAIT4EVENTTIMEOUT)
   {
    deletes.push_back(it);
   }
   printf("come back/n");
  }

  {
   std::list<std::map<PVOID,uthread*>::iterator>::iterator it = deletes.begin();
   std::list<std::map<PVOID,uthread*>::iterator>::iterator end = deletes.end();
   for( ; it != end; ++it)
   {
    if((*it)->second->status == DEAD)
    {
     DeleteFiber((*it)->first);
     m_uthreads[(*it)->second->uthread_id] = NULL;
     delete (*it)->second;
     --m_count;
    }
    m_activeList.erase(*it);
   }
  }
  //将所有等待添加到m_activeList中的纤程都添加进去
  {
   while(!m_pendingAdd.empty())
   {
    uthread *tmp = m_pendingAdd.back();
    m_pendingAdd.pop_back();
    m_activeList.insert(std::make_pair(tmp->p_uthreadContext,tmp));
   }
    
  }
   
  //看看有没有timeout的纤程
  {
   time_t now = time(NULL);
   std::list<std::pair<time_t,uthread*> >::iterator it = m_sleepList.begin();
   for( ; it != m_sleepList.end(); )
   {
    time_t t = it->first;
    if(it->first <= now)
    {
     it->second->status = ACTIVED;
     m_activeList.insert(std::make_pair(it->second->p_uthreadContext,it->second));
     it = m_sleepList.erase(it);
    }
    else
     ++it;
   }
  }

 }

 scheduler_destroy();
 ConvertFiberToThread();
 printf("scheduler end/n");
}

void Scheduler::scheduler_destroy()
{
 for(int i = 0; i < MAX_FIBER; ++i)
 {
  if(m_uthreads[i])
  {
   DeleteFiber(m_uthreads[i]->p_uthreadContext);
   delete m_uthreads[i];
  }
 }
}

uthread_t Scheduler::spawn(runnable *param,int stacksize)//创建一个新的纤程
{
 if(m_count >= MAX_FIBER)
  return -1;
 //刚创建的纤程不处于可运行状态
 PVOID uthreadcontext =  CreateFiber(stacksize,uthread::thread_routine,param);
 uthread *nthread = new uthread;
 nthread->p_uthreadContext = uthreadcontext;
 for(int i= 0; i < MAX_FIBER; ++i)
 {
  if(0 == m_uthreads[i])
  {
   nthread->uthread_id = i;
   m_uthreads[i] = nthread;
   break;
  }
 }
 add2Active(nthread);
 ++m_count;
 param->p_uthread = nthread;
 return nthread->uthread_id;
}

static int Scheduler::block(unsigned short ev,time_t timeout)
{
 m_uthreads[m_curuid]->waitevent = ev;
 if(timeout > 0)
 {
  m_uthreads[m_curuid]->status = WAIT4EVENTTIMEOUT;
  time_t t = timeout + time(NULL);
  m_sleepList.push_back(std::make_pair(t,m_uthreads[m_curuid]));
 }
 else
  m_uthreads[m_curuid]->status = WAIT4EVENT;

 SwitchToFiber(m_pUthreadContext);

 if(m_uthreads[m_curuid]->waitevent == 0)
 {
  //等待的事件到达
  return 0;
 }
 else
  return -1;//超时间

}

test.cpp

view plaincopy to clipboardprint?
// AiScheduler.cpp : 定义控制台应用程序的入口点。  
//  
 
#include "stdafx.h"  
#include "Scheduler.h"  
 
 
class test22 : public runnable  
{  
 
public:  
    void main_routine()  
    {  
        for(int i = 0 ; i < 20; ++i)  
        {  
            printf("%d/n",i);  
            printf("begin block/n");  
            if(0 == Scheduler::block(MOVE_RET,1))  
                printf("test wake me up/n");  
            else 
                printf("timeout/n");  
            //Scheduler::sleep(1);  
        }  
        printf("die/n");  
    }  
 
    uthread_t uid;  
};  
 
 
class test : public runnable  
{  
public:  
    void main_routine()  
    {  
        for(int i = 0 ; i < 10; ++i)  
        {  
            printf("%d/n",i);  
            if(t22->p_uthread->waitevent == MOVE_RET)  
                t22->p_uthread->OnEvent(MOVE_RET);  
            Scheduler::yield();  
        }  
        printf("die/n");  
    }  
    test22 *t22;  
    uthread_t uid;  
};  
 
 
 
int _tmain(int argc, _TCHAR* argv[])  
{  
    Scheduler::scheduler_init();  
      
    test22 test2;  
    test test1;  
    test1.t22 = &test2;  
    test2.uid = Scheduler::spawn(&test2,4096);  
    test1.uid = Scheduler::spawn(&test1,4096);  
      
    //test3.uid = Scheduler::spawn(&test3,4096);  
    //test4.uid = Scheduler::spawn(&test4,4096);  
    Scheduler::schedule();  
    return 0;  

 

本文来自优快云博客,转载请标明出处:http://blog.youkuaiyun.com/sniperhuangwei/archive/2010/03/28/5425471.aspx

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值