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

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

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

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

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

#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

#ifndef _SCHEDULER_H
#define _SCHEDULER_H
#include <Windows.h>#include "uthread.h"
#include <map>
#include <list>
#include <time.h>#define MAX_FIBER 8192class 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);}//将当运行权交给schedulerstatic 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

#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]));}elsem_uthreads[m_curuid]->status = WAIT4EVENT;SwitchToFiber(m_pUthreadContext);if(m_uthreads[m_curuid]->waitevent == 0){//等待的事件到达return 0;}elsereturn -1;//超时间

}

test.cpp

// 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");elseprintf("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;
}

用户态线程在AI中的应用相关推荐

  1. 线程库 c语言实现,130行C语言实现个用户态线程库——后续(一)

    130行C语言实现个用户态线程库--后续(1) ezCoroutine协程原型库只是个原型库,但是已经能够支持1000K以上数量的协程运行,而且是stackful模式.基本的对外接口有两类,一类是类似 ...

  2. 用户态线程库原理、设计与实现

    目录 一.概述 二.开发环境 三. 相关原理及算法 3.1  不得不知道的知识--c语言的函数参数传递机制 3.1.1 从汇编的角度理解C语言函数传参的方式 3.1.2 函数调用过程的堆栈变化 3.2 ...

  3. 用户态和内核态:用户态线程和内核态线程有什么区别?

    转载 文章来源于 拉钩教育 重学操作系统 林䭽 用户态和内核态:用户态线程和内核态线程有什么区别? 什么是用户态和内核态 Kernel 运行在超级权限模式(Supervisor Mode)下,所以拥有 ...

  4. golang goroutine协程概念及入门:轻量级线程(或用户态线程)

    import ("strconv""time""fmt" )

  5. 利用用户级线程提高多线程应用的性能

    随着处理器往多核的发展,多线程被越来越多的应用到软件的开发中.但是如果没有正确的使用多线程,反而可能会导致软件性能的下降. 多线程程序中一个影响程序性能的因素就是同步.对于windows系统来说,最快 ...

  6. 32位linux进程线程在内存中的样子

    1.线程诞生史 1.1 线程诞生的原因 早期是没有线程概念的,只有进程的概念,操作系统以进程为调度单位.--可以这么来理解:早期进程相当于现在的单线程的进程(只有一个线程的进程,创建进程时,里面有一个 ...

  7. Linux下的LWP(轻量级进程)、进程 、 线程、用户级线程、内核线程

    一.定义 再看正文之前我要先强调一下几点: 1. Linux中没有真正的线程,但windows中确实有线程 2. Linux中没有的线程是由进程来模拟实现的(又称作:轻量级进程) 3. 所以在Linu ...

  8. 一文弄懂printf函数从用户态到内核态的执行流程

    目录 1.简介 2.示例代码 3.程序执行初探 4.用户态处理流程 5.内核态处理流程 5.1. 软中断处理 5.2 系统调用返回 5.3 系统调用处理 5.4 stdout重定向到console 5 ...

  9. 操作系统基础知识用户态和内核态的区别

    这节课给你带来了一道非常经典的面试题目:用户态线程和内核态线程有什么区别? 这是一个组合型的问题,由很多小问题组装而成,比如: 用户态和内核态是什么? 用户级线程和内核级线程是一个怎样的对应关系? 内 ...

最新文章

  1. 我爱你,与你无关——登录系统的逻辑与结构
  2. html插入flash代码_初学者必备Web开发APP,支持代码补全,一键插入代码,厉害了!...
  3. CVPR | 旷视提出Meta-SR:单一模型实现超分辨率任意缩放因子
  4. webpack4.0各个击破(1)—— html部分
  5. Linux内核中的xx_initcall
  6. uni保存canvas图片_小程序canvas【开箱即用】
  7. AD09铺铜 (画完PCB后改线与铺铜冲突)
  8. 学习笔记之——基于深度学习的分类网络
  9. uni-app项目利用HBuilder X工具使用命令一键自动编译导出APP资源
  10. Win7自带的壁纸路径
  11. linux 文本三剑客之sed编辑器
  12. 电脑应用程序分身双开及多开(能记住账号和密码)
  13. python可视化图表分析---pyecharts库的使用
  14. Android 常用正则表达式,阿里巴巴内部spring宝典意外流出
  15. 基于Matlab/Simulink的简单三相交流系统扫频仿真
  16. CPU 缓存一致性 MESI 协议
  17. win10系统更新后图片查看器的恢复方法
  18. pylint代码检查常见警告、提示的解决方法记录
  19. 兴趣电商,是新机遇还是伪概念?
  20. HDU 6112 今夕何夕 蔡勒公式

热门文章

  1. [UVA 10827] Maximum sum on a torus
  2. 尽快安装修补程序!微软 Word 漏洞影响上百万人
  3. MDT2013折腾记-UDP端口冲突
  4. CentOS下安装JDK1.7
  5. 多彩浏览器win10版 隐私声明
  6. ORA-32004错误的解决方法
  7. 现实世界的Windows Azure:采访圣地亚哥公共安全小组的技术经理Adrian Gonzalez
  8. 黄聪:C#用正则表达式获得指定开始和结束字符串中间的一段文本
  9. 比特币现金成为第二个最有价值的区块链
  10. 比特币现金开发者:BCH网络将“提升到VISA级别”来处理交易