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會按照順序輸出

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM