Java自学者论坛

 找回密码
 立即注册

手机号码,快捷登录

恭喜Java自学者论坛(https://www.javazxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,会员资料板块,购买链接:点击进入购买VIP会员

JAVA高级面试进阶训练营视频教程

Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程Go语言视频零基础入门到精通Java架构师3期(课件+源码)
Java开发全终端实战租房项目视频教程SpringBoot2.X入门到高级使用教程大数据培训第六期全套视频教程深度学习(CNN RNN GAN)算法原理Java亿级流量电商系统视频教程
互联网架构师视频教程年薪50万Spark2.0从入门到精通年薪50万!人工智能学习路线教程年薪50万大数据入门到精通学习路线年薪50万机器学习入门到精通教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程MySQL入门到精通教程
查看: 6885|回复: 0

lua多线程共享数据的解决方案

[复制链接]
  • TA的每日心情
    奋斗
    2024-11-24 15:47
  • 签到天数: 804 天

    [LV.10]以坛为家III

    2053

    主题

    2111

    帖子

    72万

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    726782
    发表于 2021-4-8 16:37:42 | 显示全部楼层 |阅读模式

    本人表达能力有限,所以文字描述不太清晰,我更习惯自己默默地造轮子,所以我只能尽力保证我给轮子可以被直接使用。

    虽然不太会说,但有一些前提还是必要讲一下的:

    直观的讲: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会按照顺序输出

     

    哎...今天够累的,签到来了1...
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|小黑屋|Java自学者论坛 ( 声明:本站文章及资料整理自互联网,用于Java自学者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2024-12-22 19:00 , Processed in 0.065906 second(s), 30 queries .

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

    快速回复 返回顶部 返回列表