lua多线程解决方案

2 篇文章 0 订阅
1 篇文章 0 订阅

先讲下坑点:

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的锁。

 

 

 

  • 0
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值