LUA协程和多任务笔记
最近在学习lua多任务, 用于智辅游戏服务器消息的处理, 因为脚本消息很多,每分钟有几十条到上百条, 如果不能多任务并行处理, 会拖慢游戏,也不实用
整理下, 做个笔记, 以免忘了, 代码也是百度来的, 改改能用
lua用5.3.1版, 和我原来的5.1版本有不兼营, 按网上的方法改成支持中文变量
这样挺好的, 代码也不用注释, 取名字方便,也短, 不然如果用英文
背包-->bag 血-->HP 兰-->MP 还好翻译好记
怪物-->monster 人物-->human 就不有点长,不好记
地面物品-->dropeditem 装备-->useitem 狂欢应典活动-->KHQD 太不直观了
xx地图 xxNPC xx任务 就十分难翻译了,
闲话结束, 正文开始
----------------------------------------------
--LUA协程和多任务
--3个同时运行的协程为例
--每隔一定的间隔输出文字, 协程状态
--第1个 隔101毫秒 输出5次
--第2个 隔101毫秒 输出2次
--第3个 隔101毫秒 输出3次
--实现协程的关键是要 延时 = mylib.delay 这个函数
--标准库里没有这个函数 用C扩展为DLL库来使用
--先启用定时器, 然后把协程挂起(yield), 定时器时间到了再唤醒(resume)
--这样就能实现多个任务同时运行
--协程一次只能有1个运行(running), 其它都是在挂起状态(suspended)
--当协程运行结束后, 就是停止状态(dead)
--协程挂起(yield), 唤醒(resume), 应用有一定限制,就是不能在C和LUA间交叉使用
--即在C中挂起的,就要在C中唤醒, Lua中挂起的,也要在LUA中唤醒
--协程的开销不大, 初略估计了下, 开启运行2万多个协程,结束后故意不消毁,内存约增加20多M
--1个协程的开销约1k多内存
--Lua脚本
延时 = mylib.delay
function 显示(a,b)
print(a,b,'co1='..coroutine.status(co1),'co2='..coroutine.status(co2),'co3='..coroutine.status(co3))
end
function 协程(序号,次数,毫秒)
for i=1,次数 do
延时(毫秒) 显示(序号,i*毫秒 )
end
显示(序号,'结束' )
end
co1=coroutine.create(协程) coroutine.resume(co1, '脚本1', 5, 101)
co2=coroutine.create(协程) coroutine.resume(co2, '脚本2', 2, 301)
co3=coroutine.create(协程) coroutine.resume(co3, '脚本3', 3, 501)
--运行结果
036CBF54 脚本1101 co1=running co2=suspended co3=suspended
036CBF54 脚本1202 co1=running co2=suspended co3=suspended
036CC01C 脚本2301 co1=suspended co2=running co3=suspended
036CBF54 脚本1303 co1=running co2=suspended co3=suspended
036CBF54 脚本1404 co1=running co2=suspended co3=suspended
036CC0E4 脚本3501 co1=suspended co2=suspended co3=running
036CBF54 脚本1505 co1=running co2=suspended co3=suspended
036CBF54 脚本1结束 co1=running co2=suspended co3=suspended
036CC01C 脚本2602 co1=dead co2=running co3=suspended
036CC01C 脚本2结束 co1=dead co2=running co3=suspended
036CC0E4 脚本31002 co1=dead co2=dead co3=running
036CC0E4 脚本31503 co1=dead co2=dead co3=running
036CC0E4 脚本3结束 co1=dead co2=dead co3=running
--延时 = mylib.delay 的C代码 用C++Builder 6环境
--调用delay时,首先创建1个定时器
int id = SetTimer(NULL, NULL, ms, (TIMERPROC)LuaTimerProc);
--然后保存协程句柄,定时器id到TList LuaTimerList,到定时器回调时有用
LuaTimerList->Add((void*)L);
LuaTimerList->Add((void*)id);
--然后,挂起协程
--时间到了,定时器回调函数运行
--因为定时器只用一次就够了, 故销毁之
KillTimer(hWnd, uIDEvent);
--在TList LuaTimerList找到对应的记录,获得协程句柄,唤醒之
lua_resume(L, 0, 0);
--注意, 在dll退出时, 记得销毁LuaTimerList, 不然内存略有泄漏
case DLL_PROCESS_DETACH:
delete LuaTimerList;
break;
--在lua中使用方法, (编译后的库名为mylib.dll)
mylib = require('mylib')
mylib.delay(100)--延时100ms
--函数只能在协程内使用, 在主线程使用会出错
--不过没关系, 可以让所有脚本都在协程运行,按下列方式即可
function f()
--------------------
--这里插入要执行的内容
--------------------
end
co=coroutine.create(f)
coroutine.resume(co)
//---------------------------------------------------------------------------
//Lua扩展库测试
#pragma hdrstop
//---------------------------------------------------------------------------
#pragma package(smart_init)
#include <vcl.h>
#include <math.h>
#include <windows.h>
#include "LuaMylib.h"
TList *LuaTimerList;
//void _stdcall LuaTimerProc(int hwnd, int uMsg, int idEvent, int time)
void CALLBACK LuaTimerProc(HWND hWnd, UINT nMsg, UINT uIDEvent, DWORD dwTime)
{
KillTimer(hWnd, uIDEvent);
for (int i=(LuaTimerList->Count / 2) -1;i>=0; i--)
{
UINT id = (UINT)LuaTimerList->Items;
if (id == uIDEvent)
{
lua_State *L = (lua_State*)LuaTimerList->Items;
LuaTimerList->Delete(i*2);
LuaTimerList->Delete(i*2);
lua_resume(L, 0, 0);
}
}
}
static int delay (lua_State *L) {
if (lua_gettop(L) == 0) return 0;
int ms = lua_tointeger(L, 1);
int id = SetTimer(NULL, NULL, ms, (TIMERPROC)LuaTimerProc);
if (LuaTimerList == NULL )LuaTimerList = new(TList);
LuaTimerList->Add((void*)L);
LuaTimerList->Add((void*)id);
lua_pop(L, 1);//弹出参数
return lua_yield(L, 0);
}
static const luaL_Reg mylib[] = {
{"delay", delay},
{NULL, NULL}
};
/*
** Open test library
*/
LUALIB_API int luaopen_mylib (lua_State *L) {
luaL_newlib(L, mylib);
return 1;
}
--C中实现协程
--以脚务器消息接收为例
--编写一个消息处理的函数Lua(伪代码)
function Recv(Recog, Ident, Param, Tag, Series, buf, len)
--if 定义 and 定义.过滤==1 then return end;
print('收',翻译(Ident,Recog,Param,Series,Tag,len,buf))
mylib.delay(5000)
--print('协程结束')
回收协程()
end
--编写一个消息转发Lua的C函数 相当于Lua下列语句,挂到消息接收上
static _stdcall void SocketRecvHook(int buf, int len)
local co=coroutine.create(recv)
...转化内存数据到变量
coroutine.resume(co, Recog, Ident, Param, Tag, Series, buf, len)
库名.copool=co //必须, 不然协程被当垃圾回收出错, 也可以留在栈里
--如此每收到1个消息就开启1个协程, 所以协程数量增长很快,
--如果协程运行结束, 就要进行回收, 即可在C中编写,也可在LUA中
function 回收协程()
local 总数=0
local 回收=0
for i in pairs(mir.copool) do
总数 = 总数+1
if coroutine.status(mir.copool)=='dead' then
mir.copool=nil --垃圾回收器会自动回收该协程
回收=回收+1
end
end
--if 回收>0 then print('协程总数,回收,剩余=',总数,回收,总数-回收) end
end
--C代码如下
static _stdcall void SocketRecvHook(int buf, int len)
{
//调用原来的程序
OrgSocketRecvDecode(buf, len);
//调用LUA程序
PTDefaultMessage pm = (PTDefaultMessage)(buf+4);
//co=coroutine.create(recv)
lua_getglobal(L, "coroutine");//--->+1
lua_getfield(L, -1, "create"); //调用函数coroutine.create//--->+2
//获得self.recv
lua_pushlightuserdata(L, (void*)&Key); //压入地址 //--->+3
lua_gettable(L, LUA_REGISTRYINDEX);//table在栈顶 //--->+4
lua_getfield(L, -1, "recv"); //self.Recv //--->+5
lua_remove(L, -2);//移除table //--->+4
if (lua_isfunction(L, -1))
{
lua_call(L, 1, 1);//1个参数,1个结果->co //--->+2
//库名.copool=co
savepool(IntToStr(GetTickCount())+"_R_"+IntToStr(pm->Ident) );
//coroutine.resume(co,参数)
lua_getglobal(L, "coroutine");
lua_getfield(L, -1, "resume"); //调用函数coroutine.resume
lua_pushvalue(L, -3);//复制co
lua_pushinteger(L, pm->Recog);//1
lua_pushinteger(L, pm->Ident);//2
lua_pushinteger(L, pm->Param);//3
lua_pushinteger(L, pm->Tag);//4
lua_pushinteger(L, pm->Series);//5
lua_pushinteger(L, buf + 16);//6
lua_pushinteger(L, len - 16);//7
lua_call(L, 1+7, 0);//1+7个参数,0个结果->co
lua_pop(L, 3);
}
else
{
lua_pop(L, 3);
}
}
页:
[1]