先讲下坑点:
1、lua_newthread 名称存在误导性,它只是拷贝一个栈,并不是创建一个线程。
2、不同的线程使用 lua_newthread 出来的栈去调用lua代码,也要加锁,否则也会异常。
3、在lua底层有两个宏:lua_lock与lua_unlock,默认的情况下,这两个东西不起作用,lua的作者的本意是希望我们在有并发需求的时候,重写这两个宏,所以只要是底层用到这两个宏的地方,如果被我们的并发线程调用到了,而我们没有重新定义这两个宏让它加锁,就会有问题。
个人牢骚:这种方式,要求我们用加锁的方式去帮助lua维护它的垃圾回收行为,个人认为非常蠢,而且会使lua的性能下降,根据统计,非常简单的代码,也是每次执行完之后也是几十次加解锁调用,更不要说复杂的东西了。
所以要在lua源码上解决这个问题的方法:
1、扩展lua_State结构,添加锁结构指针,必须是指针,或者是类似资源id的形式,总之,必须让需求并发lua_State访问到的是同一个锁对象。
2、lua_newstate里,给锁结构分配内存,初始化锁对象
3、lua_newthread里,单纯的拷贝指针即可。
4、close_state里处理一系列锁的释放与内存释放。
因为windows和linux的锁有区别,我自己是不用lua底层管理锁对象的,我的处理方法如下。
//lstate.h 里面再lua_State里添加代码
struct lua_State {
CommonHeader;
unsigned short nci; /* number of items in 'ci' list */
lu_byte status;
StkId top; /* first free slot in the stack */
global_State *l_G;
CallInfo *ci; /* call info for current function */
const Instruction *oldpc; /* last pc traced */
StkId stack_last; /* last free slot in the stack */
StkId stack; /* stack base */
UpVal *openupval; /* list of open upvalues in this stack */
GCObject *gclist;
struct lua_State *twups; /* list of threads with open upvalues */
struct lua_longjmp *errorJmp; /* current error recover point */
CallInfo base_ci; /* CallInfo for first level (C calling Lua) */
volatile lua_Hook hook;
ptrdiff_t errfunc; /* current error handling function (stack index) */
int stacksize;
int basehookcount;
int hookcount;
unsigned short nny; /* number of non-yieldable calls in stack */
unsigned short nCcalls; /* number of nested C calls */
l_signalT hookmask;
lu_byte allowhook;
//下面的是我添加的代码
void *_user_mutex;//锁对象指针
void(*_user_lock)(void*);//lock
void(*_user_unlock)(void*);//unlock
};
//llimits.h 里,定义lua_lock的上面,添加这两行
#define lua_lock(L) if(L->_user_lock) L->_user_lock(L->_user_mutex)
#define lua_unlock(L) if(L->_user_unlock) L->_user_unlock(L->_user_mutex)
#if !defined(lua_lock)
#define lua_lock(L) ((void) 0)
#define lua_unlock(L) ((void) 0)
#endif
//lapi.h里添加函数声明
LUA_API void *lua_setmutex(lua_State *L,
void *ptr, void(*lock_func)(void*),
void(*unlock_func)(void*));
//lapi.c里添加函数定义
LUA_API void *lua_setmutex(lua_State *L,
void *ptr, void(*lock_func)(void*),
void(*unlock_func)(void*))
{
void *_Result = L->_user_mutex;
L->_user_mutex = ptr;
L->_user_lock = lock_func;
L->_user_unlock = unlock_func;
return _Result;
}
//lstate.c里
LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) {
int i;
lua_State *L;
global_State *g;
LG *l = cast(LG *, (*f)(ud, NULL, LUA_TTHREAD, sizeof(LG)));
if (l == NULL) return NULL;
L = &l->l.l;
g = &l->g;
L->next = NULL;
L->tt = LUA_TTHREAD;
g->currentwhite = bitmask(WHITE0BIT);
L->marked = luaC_white(g);
preinit_thread(L, g);
g->frealloc = f;
g->ud = ud;
g->mainthread = L;
g->seed = makeseed(L);
g->gcrunning = 0; /* no GC while building state */
g->GCestimate = 0;
g->strt.size = g->strt.nuse = 0;
g->strt.hash = NULL;
setnilvalue(&g->l_registry);
g->panic = NULL;
g->version = NULL;
g->gcstate = GCSpause;
g->gckind = KGC_NORMAL;
g->allgc = g->finobj = g->tobefnz = g->fixedgc = NULL;
g->sweepgc = NULL;
g->gray = g->grayagain = NULL;
g->weak = g->ephemeron = g->allweak = NULL;
g->twups = NULL;
g->totalbytes = sizeof(LG);
g->GCdebt = 0;
g->gcfinnum = 0;
g->gcpause = LUAI_GCPAUSE;
g->gcstepmul = LUAI_GCMUL;
L->_ptr_ud_ex = NULL;
/*这里把这3个指针初始化为null*/
L->_user_mutex = NULL;
L->_user_lock = NULL;
L->_user_unlock = NULL;
for (i=0; i < LUA_NUMTAGS; i++) g->mt[i] = NULL;
if (luaD_rawrunprotected(L, f_luaopen, NULL) != LUA_OK) {
/* memory allocation error: free partial state */
close_state(L);
L = NULL;
}
return L;
}
LUA_API void lua_close (lua_State *L) {
L = G(L)->mainthread; /* only the main thread can be closed */
//lua_lock(L);
//close_state(L);
//将代码改成这样子
void *mtx = L->_user_mutex;
void (*mtx_lock)(void*) = L->_user_lock;
void (*mtx_unlock)(void*) = L->_user_unlock;
if (mtx && mtx_lock && mtx_unlock){
mtx_lock(mtx);
close_state(L);
mtx_unlock(mtx);
}
else
{
close_state(L);
}
}
LUA_API lua_State *lua_newthread (lua_State *L) {
global_State *g = G(L);
lua_State *L1;
lua_lock(L);
luaC_checkGC(L);
/* create new thread */
L1 = &cast(LX *, luaM_newobject(L, LUA_TTHREAD, sizeof(LX)))->l;
L1->marked = luaC_white(g);
L1->tt = LUA_TTHREAD;
/* link it on list 'allgc' */
L1->next = g->allgc;
g->allgc = obj2gco(L1);
/* anchor it on L stack */
setthvalue(L, L->top, L1);
api_incr_top(L);
preinit_thread(L1, g);
L1->hookmask = L->hookmask;
L1->basehookcount = L->basehookcount;
L1->hook = L->hook;
//这里直接拷贝指针即可
L1->_user_mutex = L->_user_mutex;
L1->_user_lock = L->_user_lock;
L1->_user_unlock = L->_user_unlock;
resethookcount(L1);
/* initialize L1 extra space */
memcpy(lua_getextraspace(L1), lua_getextraspace(g->mainthread),
LUA_EXTRASPACE);
luai_userstatethread(L, L1);
stack_init(L1, L); /* init stack */
lua_unlock(L);
return L1;
}
//最后我在C++上层写这两个
static void luamutex_lock(void *_MtxObj) {
((std::recursive_mutex*)_MtxObj)->lock();
}
static void luamutex_unlock(void *_MtxObj) {
((std::recursive_mutex*)_MtxObj)->unlock();
}
std::recursive_mutex _Mtx;//保险一点用递归锁
//在创建主对象(lua_newstate)完成后,创建子对象(lua_newthread)之前,调用:
lua_setmutex(MainState, &_Mtx, luamutex_lock, luamutex_unlock);
最后,锁的性能:
windows下: std::mutex系列的性能比操作系统CRITICAL_SECTION高
linux下: pthread_mutext_t 性能比g++(8.2) std::mutex系列要高。
windows 下:std::mutex的性能跟linux pthread_mutext_t相差不大,几乎一样的性能。
所以,在windows下,应该使用#include <mutex>里的东西,而在linux应该使用pthread的锁。