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.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会按照顺序输出

 

posted @ 2019-10-23 04:43  babypapa  阅读(4764)  评论(0编辑  收藏  举报