lua多线程共享数据的解决方案
本人表达能力有限,所以文字描述不太清晰,我更习惯自己默默地造轮子,所以我只能尽力保证我给轮子可以被直接使用。
虽然不太会说,但有一些前提还是必要讲一下的:
直观的讲:lua并不支持多线程,任何尝试用lua做并发方案的人,都有病,没错,我自己也是。
lua有并发需求本身就是一件很鬼扯的事,本身会有这种需求,就说明可能在项目架构的大方向上,存在了问题。
我认为对于C/C++程序员来说,我们看中lua的地方是,它能够用最小的代价与C/C++交互,能够由C/C++去弥补它的不足,
所以,它能够包容所有对它理解程度不一样的C++程序员,
你不会用lua来解决一个问题,没关系,你懂得C/C++的办法,你给lua公开一套接口,说不定解决的还更完美。
虽然从另外一个角度来看,这是一种脱裤子放屁的行为,
但你得知道,维护C++代码的代价,与维护lua代码的代价是不同的,C++乱了,你自己解决起来可能都是大问题,而lua是不怕乱的,除非是写C++的人乱搞。
所以说这么多,我个人有这种需求的理由是:我正在用lua来做并发服务端的业务逻辑,已经跑题太多了,我只简单说一下:
C++实现套接字IO >> 传递json作为文本协议 >> 解析成lua table >> lua执行业务逻辑
在lua执行业务逻辑的过程中,就对共享数据有需求了,例如用户login之后的数据由lua来管理。
解决方案共两种:
1、基于lua_newthread创建lua子对象,重定义lua源码中的lua_lock与lua_unlock宏。
优点:这种方案的外表是完美无缺的。
缺点:降低lua的整体运行速度,因为我们使用了加锁的方式去维护lua的gc机制,并且我个人认为代价很大。
这种方案是我最初设计方案,但由于不能忍受一次请求上百次加锁操作,我最终放弃了这个方案。
我懒,不想把这种已经放弃掉的方案往这边搬了,如果非常有必要,劳您移驾传送门。
2、将共享数据存储在C/C++或一个公共的lua_State对象中,利用lua元表实现共享table存取逻辑。
优点:具有最高的可维护性,因为是基于lua api实现,和lua版本无关。
缺点:局限性最大。
这是我目前正在使用的方案,如下:
请注意,本例基于c++17标准实现,用到了 std::variant
否则,请自行实现支持hash的变体结构,或者使用一个公共lua_State对象来实现交互逻辑。
在Microsoft Visual Studio中,C++标准版本的设置在:项目 - 属性 - C/C++ - 语言 - C++语言标准中
使用g++时,可以在命令行中直接设置。
由于之前的代码存在许多不安全因素,因此我重构了一次代码(2020/07/27),以下是全新的代码,库代码仅一个文件:lxsharelib.hpp
C++中使用 lua_openxsharelib(L);
lua中:
local xst = xshare.new(name); -- 创建一个共享数据表
xshare.pcall(xst,function[,args...]); -- 基本与lua的pcall保持一致,但是在执行function之前,会先进行加锁
xshare.istab(xst); -- 判断一个变量是不是共享数据表,在C++中通过upvalue上存放的元表指针 与 xst的元表指针进行对比来确定。
xshare.islife(xst); -- 判断一个共享数据表是否还存活,xst本身只是一种引用对象,如果有其他线程将xst指向的共享表删除了,xst也就同时死亡了。
local t = xshare.swap(xst[, luatable]); -- 可与lua table交换数据,返回值是旧的共享数据表完全拉取的lua table,如果不指定参数2,就是单纯的完整拉取共享数据表到返回值的table中
xshare.clear(xst); -- 清空一个共享数据表,类似 t={}的机制
其余机制基本与lua table操作一致,共享数据表支持的index类型(number boolean string),支持的value类型(number boolean string table)
示例:
xst.a = 100;
xst.b = {1,2,3}
这次的一些实现细节和变化:
共享table操作的过程中,增删改查都是加锁的,所以,已经不再提供lock unlock这种接口,这么改的理由是lua难以保证lock unlock对称执行。
xshare.pcall 与 xshare.swap 相结合,在并发环境下,能够实现类似事务处理的机制。
给lua的数据引用对象由之前的table换成了userdata,同时强制依赖的预定义的元方法。
这就表示,xshare共享数据表基本丧失了可重定义元方法的机制,
如果非要重定义元方法,需要重新设置所有xshare在C++向lua提供的函数(包括元方法)中的第1个upvalue,这个upvalue就是预定义的元表。
在上一次我写的代码中,单纯的就是保留容器指针,这是最重要的不安全因素。
一个线程将一个子共享表设置为nil时,如果另一个线程中,还保留了这个子表的引用指针,就导致了这个指针悬垂。
为了解决这个问题:
1、我在根共享表下保存了一个引用对象指针链表,根共享表是由xshare.new创建的,常规的情况下不能被删除的,只能做清空处理。
2、由lua userdata保存引用对象,引用对象包含了共享数据容器指针,引用对象指针链表的迭代器。
3.1、为userdata提供 __gc 回调,__gc回调触发时,将引用对象指针从链表中删除,并将迭代器设置为end()
3.2、当其他线程将子共享表设置为nil时,共享表对象析构时遍历这个链表,将对应容器指针的引用全部从链表中删除,并将引用对象内的迭代器设置为end()。
这么做,C++中的数据表指针与引用对象就形成了闭环,任意一方对共享表进行删除操作,都会导致所有引用失效。
#pragma once #include "lua.hpp" #include <mutex> #include <variant> #include <unordered_map> #include <string> namespace lxshare_internal { // 两个用于加锁的类定义,在g++下,应该自己用操作系统实现这两个类 using _MtxTy = typename std::recursive_mutex; using _LockTy = typename std::lock_guard<_MtxTy>; using xbool = unsigned char; using xuserdata = unsigned long long*; class xtable; using xvalue = std::variant<std::string, intptr_t, double, xbool, xtable*, xuserdata>; using xkey = std::variant<std::string, intptr_t, double, xbool, xuserdata>; static bool __xkey_types[10] = { false, // LUA_TNONE false, // LUA_TNIL true, // LUA_TBOOLEAN true, //LUA_TLIGHTUSERDATA true, //LUA_TNUMBER true, //LUA_TSTRING false, //LUA_TTABLE false, //LUA_TFUNCTION false, //LUA_TUSERDATA false, //LUA_TTHREAD }; static bool _islxkey(int _Type) { return __xkey_types[_Type + 1]; } static xkey _lxkey(lua_State* s, int idx) { int _Type = lua_type(s, idx); switch (_Type) { case LUA_TNUMBER: if (!lua_isinteger(s, idx)) return lua_tonumber(s, idx); else return lua_tointeger(s, idx); case LUA_TSTRING: return lua_tostring(s, idx); case LUA_TBOOLEAN: return (xbool)lua_toboolean(s, idx); case LUA_TLIGHTUSERDATA: return (xuserdata)lua_touserdata(s, idx); } return (intptr_t)0; } class xtable { public: class reftype; using _Tabty = typename std::unordered_map<xkey, xvalue>; using _RefIterator = typename _Tabty::iterator; struct _RSTAT { _MtxTy mtx; std::list<reftype*> refs; xtable* root = nullptr; }; class reftype { public: xtable* ref() { return (v != rs->refs.end()) ? t : nullptr; } void unref() { auto& refs = rs->refs; if (v == refs.end()) return; refs.erase(v); v = refs.end(); } _MtxTy& mtx() { return rs->mtx; } xtable* t; std::list<reftype*>::iterator v; _RSTAT* rs; }; xtable() { // 只有在创建根共享表时,才会用这个无参数的默认构造 iMax = 0; rs = new _RSTAT; rs->root = this; } xtable(xtable* t) { // 创建子共享表的构造 iMax = 0; rs = t->rs; } ~xtable() { for (auto it = rs->refs.begin(); it != rs->refs.end(); ) { reftype* p = *it; if (p->t == this) { p->v = rs->refs.end(); it = rs->refs.erase(it); } else ++it; } rs->refs.clear(); if (rs->root != this) { clear(); return; } } _RefIterator begin() { return tab.begin(); } _RefIterator end() { return tab.end(); } _RefIterator find(const xkey& k) { return tab.find(k); } void clear() { for (auto it = tab.begin(); it != tab.end(); ++it) { if (it->second.index() == 4) delete (std::get<4>(it->second)); } tab.clear(); } void new_table(lua_State* s, const char *_Name) { _LockTy lg(rs->mtx); auto it = tab.find(_Name); if (it == tab.end()) it = tab.insert({ lua_tostring(s, 1), new xtable(this) }).first; std::get<4>(it->second)->put_ref(s); } void get_table(lua_State* s, xkey &k) { auto it = tab.find(k); if (it == tab.end()) { put_nil(s); return; } put_val(s, it->second); } int set_table(lua_State* s, xkey& k, int n) { auto it = tab.find(k); if (it == tab.end()) { if (lua_isnil(s, n)) { // tab.key = nil; return 0; } it = tab.insert({ k, xvalue() }).first; int rc = xtable::take_val(s, n, it->second); if (rc) tab.erase(it); else imax_check(k); return rc; } else { // 已经存在的key if (lua_isnil(s, n)) { if (it->first.index() == 1) { if (std::get<1>(it->first) == iMax) iMax--; } tab.erase(it); return 0; } if (it->second.index() == 4) { // 如果它是table xtable* t = std::get<4>(it->second); // 如果当前要插入的值也是table,就只对它做清空动作,否则就删除它 if (lua_istable(s, n)) t->clear(); else delete t; } int rc = xtable::take_val(s, n, it->second); if (rc) tab.erase(it); return rc; } } intptr_t getn() { // 基本思路是,只有最坏的情况,才开始用二叉查找法去计算当前最大的一个数字key if (iMax == 0) return 0; intptr_t k = iMax; auto it = tab.find(xkey((intptr_t)k)); if (it != tab.end()) return k; intptr_t n = 0; while (k - n) { intptr_t m = (k + n) >> 1; auto it = tab.find(xkey((intptr_t)m)); if (it == tab.end()) k = m; else n = m; } iMax = n; return n; } int swap_tab(lua_State* s, int n) { tab.clear(); return take_tab(s, n); } void toluatable(lua_State* s) { lua_createtable(s, (int)tab.size(), 0); for (auto it = tab.begin(); it != tab.end(); ++it) { put_key(s, it->first); if (it->second.index() == 4) std::get<4>(it->second)->toluatable(s); else put_val(s, it->second); lua_settable(s, -3); } } private: void imax_check(xkey& k) { if (k.index() == 1) { size_t n = std::get<1>(k); if (n > iMax) iMax = n; } } static int take_val(lua_State* s, int n, xvalue &v) { int _Type = lua_type(s, n); xtable* t = nullptr; switch (_Type) { case LUA_TBOOLEAN: v = (xbool)lua_toboolean(s, n); break; case LUA_TNUMBER: if (lua_isinteger(s, n)) v = lua_tointeger(s, n); else v = lua_tonumber(s, n); break; case LUA_TSTRING: v = lua_tostring(s, n); break; case LUA_TTABLE: t = new xtable; v = t; return t->take_tab(s, (n > 0) ? n : -2); case LUA_TLIGHTUSERDATA: v = (xuserdata)lua_touserdata(s, n); break; default: return (n < 0) ? -2 : -3; } return 0; } int take_tab(lua_State* s, int n) { lua_pushnil(s); int _Result = 0; while (lua_next(s, n)) { if (!_islxkey(lua_type(s, -2))) { lua_pop(s, 2); return -1; } xkey key = _lxkey(s, -2); imax_check(key); auto itval = tab.insert({ key, xvalue() }).first; int rc = xtable::take_val(s, -1, itval->second); lua_pop(s, 1); if (rc) return rc; } return 0; } private: void put_ref(lua_State* s) { reftype* p = (reftype*)lua_newuserdata(s, sizeof(reftype)); rs->refs.push_front(p); *p = { this, rs->refs.begin(), rs }; lua_pushvalue(s, lua_upvalueindex(1)); lua_setmetatable(s, -2); } public: static void put_nil(lua_State* s) { lua_pushnil(s); } static void put_val(lua_State* s, xvalue &v) { switch (v.index()) { case 0: lua_pushstring(s, std::get<0>(v).c_str()); break; case 1: lua_pushinteger(s, std::get<1>(v)); break; case 2: lua_pushnumber(s, std::get<2>(v)); break; case 3: lua_pushboolean(s, (bool)std::get<3>(v)); break; case 4: std::get<4>(v)->put_ref(s); break; case 5: lua_pushlightuserdata(s, std::get<5>(v)); break; default: lua_pushnil(s); break; } } static void put_key(lua_State* s, const xkey& k) { switch (k.index()) { case 0: lua_pushstring(s, std::get<0>(k).c_str()); break; case 1: lua_pushinteger(s, std::get<1>(k)); break; case 2: lua_pushnumber(s, std::get<2>(k)); break; case 3: lua_pushboolean(s, (bool)std::get<3>(k)); break; case 4: lua_pushlightuserdata(s, std::get<4>(k)); break; default: lua_pushnil(s); break; } } private: _Tabty tab; size_t iMax; _RSTAT *rs; friend int xshare_new(lua_State* s); }; // 这里的xRoot是一个仅在C++中存在的根共享表 static xtable xRoot; // __gc static int xtable_gc(lua_State* s) { auto _Ref = (xtable::reftype*)lua_touserdata(s, 1); auto& _Mtx = _Ref->mtx(); _Mtx.lock(); _Ref->unref(); _Mtx.unlock(); return 0; } // __index static int xtable_index(lua_State* s) { auto _Ref = (xtable::reftype*)lua_touserdata(s, 1); auto& _Mtx = _Ref->mtx(); _Mtx.lock(); auto t = _Ref->ref(); if (!t) { _Mtx.unlock(); return luaL_error(s, "xshare table __index: 共享数据表引用已失效!"); } int _Type = lua_type(s, 2); if (!_islxkey(_Type)) { _Mtx.unlock(); return luaL_error(s, "xshare table __index: 不支持类型 %s 作为索引! (支持的类型为 number string boolean lightuserdata)", lua_typename(s, _Type)); } xkey k = _lxkey(s, 2); t->get_table(s, k); _Mtx.unlock(); return 1; } // __newindex static int xtable_newindex(lua_State* s) { auto _Ref = (xtable::reftype*)lua_touserdata(s, 1); auto& _Mtx = _Ref->mtx(); _Mtx.lock(); auto t = _Ref->ref(); if (!t) { _Mtx.unlock(); return luaL_error(s, "xshare table __newindex: 共享数据表引用已失效!"); } int _Type = lua_type(s, 2); if (!_islxkey(_Type)) { _Mtx.unlock(); return luaL_error(s, "xshare table __newindex: 不支持类型 %s 作为索引! (支持的类型为 number string boolean lightuserdata)", lua_typename(s, _Type)); } xkey k = _lxkey(s, 2); int rc = t->set_table(s, k, 3); _Mtx.unlock(); if (rc == -1) return luaL_error(s, "xshare table __newindex: 字段中存在无效的索引类型"); else if (rc == -2) return luaL_error(s, "xshare table __newindex: 字段中存在无效的共享类型"); else if (rc == -3) return luaL_error(s, "xshare table __newindex: 无效的共享类型(支持的类型为 boolean number string table lightuserdata)"); return 0; } // __len static int xtable_len(lua_State* s) { auto _Ref = (xtable::reftype*)lua_touserdata(s, 1); auto& _Mtx = _Ref->mtx(); _Mtx.lock(); auto t = _Ref->ref(); if (!t) { _Mtx.unlock(); return luaL_error(s, "xshare table __len: 共享数据表引用已失效!"); } lua_pushinteger(s, t->getn()); _Mtx.unlock(); return 1; } // __pairs static int xtable_pairs(lua_State* s) { lua_pushvalue(s, lua_upvalueindex(1)); lua_pushcclosure(s, [](lua_State* s)->int { auto _Ref = (xtable::reftype*)lua_touserdata(s, 1); auto& _Mtx = _Ref->mtx(); _Mtx.lock(); auto t = _Ref->ref(); if (!t) { _Mtx.unlock(); return luaL_error(s, "xshare table __newindex: 共享数据表引用已失效!"); } auto it = t->end(); int _Type = lua_type(s, 2); if (lua_gettop(s) > 1 && _Type != LUA_TNIL) { if (_islxkey(_Type)) it = t->find(_lxkey(s, 2)); } if (it != t->end()) ++it; else it = t->begin(); if (it == t->end()) { _Mtx.unlock(); return 0; } xtable::put_key(s, it->first); xtable::put_val(s, it->second); _Mtx.unlock(); return 2; }, 1); lua_pushvalue(s, 1); lua_pushnil(s); return 3; } // xshare.new(name) 创建根共享表 static int xshare_new(lua_State* s) { xRoot.rs->mtx.lock(); if (!lua_gettop(s)) { xRoot.rs->mtx.unlock(); return luaL_error(s, "xshare.new 需要一个有效的string索引值"); } if (lua_type(s, 1) != LUA_TSTRING) { xRoot.rs->mtx.unlock(); return luaL_error(s, "xshare.new 需要一个有效的string索引值"); } xRoot.new_table(s, lua_tostring(s, 1)); xRoot.rs->mtx.unlock(); return 1; } static bool _isxtab(lua_State* s, int n) { if (lua_type(s, n) != LUA_TUSERDATA) return false; lua_getmetatable(s, n); bool _Result = (lua_topointer(s, -1) == lua_topointer(s, lua_upvalueindex(1))); lua_pop(s, 1); return _Result; } // xshare.pcall(xshare_table) 基本用法等同于pcall static int xshare_pcall(lua_State* s) { if (lua_gettop(s) < 2) return luaL_error(s, "xshare.pcall 参数错误:请参考:xshare.pcall(xshare_table, function [, args])"); if (!_isxtab(s, 1)) return luaL_error(s, "xshare.pcall 参数错误:请参考:xshare.pcall(xshare_table, function [, args])"); if (lua_type(s, 2) != LUA_TFUNCTION) return luaL_error(s, "xshare.pcall 参数错误:请参考:xshare.pcall(xshare_table, function [, args])"); auto _Ref = (xtable::reftype*)lua_touserdata(s, 1); auto& _Mtx = _Ref->mtx(); _Mtx.lock(); int _Top = lua_gettop(s); int _Argc = _Top - 2; lua_pushvalue(s, 2); for (int i = 1; i <= _Argc; i++) lua_pushvalue(s, _Argc + i); if (lua_pcall(s, _Argc, LUA_MULTRET, 0)) { _Mtx.unlock(); lua_pushboolean(s, false); lua_insert(s, -2); return 2; } _Mtx.unlock(); lua_pushboolean(s, true); int _Resultc = lua_gettop(s) - _Top; if (_Resultc > 0) lua_insert(s, -(_Resultc)); return _Resultc; } // xshare.istab(xshare_table) 判断一个变量是不是一个共享数据表 static int xshare_istab(lua_State* s) { if (lua_gettop(s) < 1) { lua_pushboolean(s, false); return 1; } lua_pushboolean(s, _isxtab(s, 1)); return 1; } // xshare.islife(xshare_table) 判断一个共享数据表是否还有效 static int xshare_islife(lua_State* s) { if (lua_gettop(s) < 1) { lua_pushboolean(s, false); return 1; } if (!_isxtab(s, 1)) { lua_pushboolean(s, false); return 1; } auto _Ref = (xtable::reftype*)lua_touserdata(s, 1); auto& _Mtx = _Ref->mtx(); _Mtx.lock(); bool _Result = (_Ref->ref() != nullptr); _Mtx.unlock(); lua_pushboolean(s, _Result); return 1; } // xshare.swap(xshare_table[,table]) 可与lua table进行数据交换 static int xshare_swap(lua_State* s) { int _Top = lua_gettop(s); if (_Top < 1) return luaL_error(s, "xshare.swap 参数错误:请参考:xshare.swap(xshare_table[,table])"); if (!_isxtab(s, 1)) return luaL_error(s, "xshare.swap 参数错误:请参考:xshare.swap(xshare_table[,table])"); auto _Ref = (xtable::reftype*)lua_touserdata(s, 1); auto& _Mtx = _Ref->mtx(); _Mtx.lock(); auto t = _Ref->ref(); if (!t) { _Mtx.unlock(); return luaL_error(s, "xshareswap: 共享数据表引用已失效!"); } t->toluatable(s); if (_Top > 1) { if (lua_istable(s, 2)) { int rc = t->swap_tab(s, 2); _Mtx.unlock(); if (rc == -1) return luaL_error(s, "xshare table __newindex: 字段中存在无效的索引类型"); else if (rc == -2) return luaL_error(s, "xshare table __newindex: 字段中存在无效的共享类型"); else if (rc == -3) return luaL_error(s, "xshare table __newindex: 无效的共享类型(支持的类型为 boolean number string table lightuserdata)"); } else { _Mtx.unlock(); return luaL_error(s, "xshare.swap(xshare_table, table) 参数2必须是一个lua table"); } } else { _Mtx.unlock(); } return 1; } // xshare.clear(xshare_table) 清空一个共享数据表 static int xshare_clear(lua_State* s) { int _Top = lua_gettop(s); if (_Top < 1) return luaL_error(s, "xshare.clear 参数错误:请参考:xshare.clear(xshare_table)"); if (!_isxtab(s, 1)) return luaL_error(s, "xshare.clear 参数错误:请参考:xshare.clear(xshare_table)"); auto _Ref = (xtable::reftype*)lua_touserdata(s, 1); auto& _Mtx = _Ref->mtx(); _Mtx.lock(); auto t = _Ref->ref(); if (!t) { _Mtx.unlock(); return luaL_error(s, "xshare.clear: 共享数据表引用已失效!"); } t->clear(); _Mtx.unlock(); return 0; } static int xshare_mutex(lua_State* s) { if (lua_gettop(s) < 2) return luaL_error(s, "xshare.mutex 参数错误:请参考:xshare.mutex(xshare_table, function)"); if (!_isxtab(s, 1)) return luaL_error(s, "xshare.mutex 参数错误:请参考:xshare.mutex(xshare_table, function)"); if (lua_type(s, 2) != LUA_TFUNCTION) return luaL_error(s, "xshare.mutex 参数错误:请参考:xshare.mutex(xshare_table, function)"); lua_pushvalue(s, 1); lua_pushvalue(s, 2); lua_pushcclosure(s, [](lua_State* s)->int { auto _Ref = (xtable::reftype*)lua_touserdata(s, lua_upvalueindex(1)); auto& _Mtx = _Ref->mtx(); _Mtx.lock(); lua_pushvalue(s, lua_upvalueindex(2)); int _Top = lua_gettop(s); for (int i = 1; i <= _Top; i++) lua_pushvalue(s, i); if (lua_pcall(s, _Top, LUA_MULTRET, 0)) { _Mtx.unlock(); return lua_error(s); } _Mtx.unlock(); return lua_gettop(s) - _Top; }, 2); return 1; } } static int luaL_openxsharelib(lua_State* s) { lua_getglobal(s, "xshare"); if (lua_istable(s, -1)) { lua_pop(s, 1); return 0; } lua_pop(s, 1); luaL_Reg funcs[] = { {"new",lxshare_internal::xshare_new}, {"pcall", lxshare_internal::xshare_pcall }, {"istab", lxshare_internal::xshare_istab }, {"islife", lxshare_internal::xshare_islife }, {"swap", lxshare_internal::xshare_swap }, {"clear", lxshare_internal::xshare_clear}, {"mutex", lxshare_internal::xshare_mutex}, {NULL,NULL}, }; luaL_newlibtable(s, funcs); luaL_Reg metatab_funcs[] = { { "__gc", lxshare_internal::xtable_gc }, { "__index", lxshare_internal::xtable_index }, { "__newindex", lxshare_internal::xtable_newindex }, { "__len", lxshare_internal::xtable_len }, { "__pairs", lxshare_internal::xtable_pairs }, {NULL,NULL}, }; luaL_newlibtable(s, metatab_funcs); lua_pushvalue(s, -1); luaL_setfuncs(s, metatab_funcs, 1); luaL_setfuncs(s, funcs, 1); lua_setglobal(s, "xshare"); return 0; }
简单的测试代码:
C++:
#include <iostream> #include "lua.hpp" #pragma comment(lib, "lua54.lib") #include "lxsharelib.hpp" void test_lxsharelib(int thread_index) { lua_State* s = luaL_newstate(); luaL_openlibs(s); lua_openxsharelib(s); int rc = luaL_dofile(s, "xshare.lua"); if (rc) { printf("%s\n", lua_tostring(s, -1)); return; } lua_pushinteger(s, thread_index); lua_setglobal(s, "thread_index"); lua_getglobal(s, "Foo1"); int fn = luaL_ref(s, LUA_REGISTRYINDEX); for (int i = 0; i < 5; i++) { lua_rawgeti(s, LUA_REGISTRYINDEX, fn); lua_call(s, 0, 0); } luaL_unref(s, LUA_REGISTRYINDEX, fn); lua_close(s); } int main() { for (int i = 0; i < 3; i++) std::thread(test_lxsharelib, i).detach(); system("pause"); return 0; }
lua:
-- xshare.lua -- xshare.new(name) 创建一个共享数据表 local xst = xshare.new('test share table') -- xshare.pcall 与 lua 的 pcall 行为完全一致,但在调用之前会使用xst中的锁对象加锁 xshare.pcall(xst, function () if xst.a == nil then xst.a = 1; end end); -- xshare.mutex(xst, function) 会返回一个函数,函数调用时加锁, -- 它不是pcall的机制,不会返回是否调用成功和错误信息,一旦调用过程有错误,它会解锁,报错。 Foo1 = xshare.mutex(xst, function() print("(thread" .. tostring(thread_index) .. "):" .. tostring(xst.a)); xst.a = xst.a + 1; end); -- 这个测试脚本代码,预期的输出: --(threadx):y --x从0到2,共3个线程 --y从1到15,每个线程调用5次Foo1 --y会按照顺序输出