本人表達能力有限,所以文字描述不太清晰,我更習慣自己默默地造輪子,所以我只能盡力保證我給輪子可以被直接使用。
雖然不太會說,但有一些前提還是必要講一下的:
直觀的講: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會按照順序輸出