用户态线程在AI中的应用
最近这段时间在修改服务器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中的应用相关推荐
- 线程库 c语言实现,130行C语言实现个用户态线程库——后续(一)
130行C语言实现个用户态线程库--后续(1) ezCoroutine协程原型库只是个原型库,但是已经能够支持1000K以上数量的协程运行,而且是stackful模式.基本的对外接口有两类,一类是类似 ...
- 用户态线程库原理、设计与实现
目录 一.概述 二.开发环境 三. 相关原理及算法 3.1 不得不知道的知识--c语言的函数参数传递机制 3.1.1 从汇编的角度理解C语言函数传参的方式 3.1.2 函数调用过程的堆栈变化 3.2 ...
- 用户态和内核态:用户态线程和内核态线程有什么区别?
转载 文章来源于 拉钩教育 重学操作系统 林䭽 用户态和内核态:用户态线程和内核态线程有什么区别? 什么是用户态和内核态 Kernel 运行在超级权限模式(Supervisor Mode)下,所以拥有 ...
- golang goroutine协程概念及入门:轻量级线程(或用户态线程)
import ("strconv""time""fmt" )
- 利用用户级线程提高多线程应用的性能
随着处理器往多核的发展,多线程被越来越多的应用到软件的开发中.但是如果没有正确的使用多线程,反而可能会导致软件性能的下降. 多线程程序中一个影响程序性能的因素就是同步.对于windows系统来说,最快 ...
- 32位linux进程线程在内存中的样子
1.线程诞生史 1.1 线程诞生的原因 早期是没有线程概念的,只有进程的概念,操作系统以进程为调度单位.--可以这么来理解:早期进程相当于现在的单线程的进程(只有一个线程的进程,创建进程时,里面有一个 ...
- Linux下的LWP(轻量级进程)、进程 、 线程、用户级线程、内核线程
一.定义 再看正文之前我要先强调一下几点: 1. Linux中没有真正的线程,但windows中确实有线程 2. Linux中没有的线程是由进程来模拟实现的(又称作:轻量级进程) 3. 所以在Linu ...
- 一文弄懂printf函数从用户态到内核态的执行流程
目录 1.简介 2.示例代码 3.程序执行初探 4.用户态处理流程 5.内核态处理流程 5.1. 软中断处理 5.2 系统调用返回 5.3 系统调用处理 5.4 stdout重定向到console 5 ...
- 操作系统基础知识用户态和内核态的区别
这节课给你带来了一道非常经典的面试题目:用户态线程和内核态线程有什么区别? 这是一个组合型的问题,由很多小问题组装而成,比如: 用户态和内核态是什么? 用户级线程和内核级线程是一个怎样的对应关系? 内 ...
最新文章
- 我爱你,与你无关——登录系统的逻辑与结构
- html插入flash代码_初学者必备Web开发APP,支持代码补全,一键插入代码,厉害了!...
- CVPR | 旷视提出Meta-SR:单一模型实现超分辨率任意缩放因子
- webpack4.0各个击破(1)—— html部分
- Linux内核中的xx_initcall
- uni保存canvas图片_小程序canvas【开箱即用】
- AD09铺铜 (画完PCB后改线与铺铜冲突)
- 学习笔记之——基于深度学习的分类网络
- uni-app项目利用HBuilder X工具使用命令一键自动编译导出APP资源
- Win7自带的壁纸路径
- linux 文本三剑客之sed编辑器
- 电脑应用程序分身双开及多开(能记住账号和密码)
- python可视化图表分析---pyecharts库的使用
- Android 常用正则表达式,阿里巴巴内部spring宝典意外流出
- 基于Matlab/Simulink的简单三相交流系统扫频仿真
- CPU 缓存一致性 MESI 协议
- win10系统更新后图片查看器的恢复方法
- pylint代码检查常见警告、提示的解决方法记录
- 兴趣电商,是新机遇还是伪概念?
- HDU 6112 今夕何夕 蔡勒公式
热门文章
- [UVA 10827] Maximum sum on a torus
- 尽快安装修补程序!微软 Word 漏洞影响上百万人
- MDT2013折腾记-UDP端口冲突
- CentOS下安装JDK1.7
- 多彩浏览器win10版 隐私声明
- ORA-32004错误的解决方法
- 现实世界的Windows Azure:采访圣地亚哥公共安全小组的技术经理Adrian Gonzalez
- 黄聪:C#用正则表达式获得指定开始和结束字符串中间的一段文本
- 比特币现金成为第二个最有价值的区块链
- 比特币现金开发者:BCH网络将“提升到VISA级别”来处理交易