本人表达能力有限,所以文字描述不太清晰,我更习惯自己默默地造轮子,所以我只能尽力保证我给轮子可以被直接使用。
虽然不太会说,但有一些前提还是必要讲一下的:
直观的讲: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.mutex(xst, func); -- 会返回一个新的函数,这个新的函数被调用时加锁,然后调用func。
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会按照顺序输出
|