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++时,可以在命令行中直接设置。

 

//lxsharelib.h
#pragma once
#include "lua/lua.hpp"

/*
lua_openxsharelib 向一个lua_State中提供一个名为:xshare的table
提供以下接口:
xshare.new(key); -- 返回一个和key关联的共享table副本,如果指定的key是已存在的共享table,则引用它,否则就创建它。key类型可以是整数,浮点数,字符串

xshare.lock(xshare_table); -- 锁定共享table
xshare.unlock(xshare_table); -- 解锁共享table
xshare.mutex(xshare_table, func); --func是回调函数,也就是一种自动加解锁的模式,用来应对那种可能在函数中很多地方需要返回的加解锁处理的方案
示例:
xshare.mutex(xst, function(_Tab)
    for i, e in pairs(_Tab) do
        print(i..‘:‘..e);
    end
end);


local ret = xshare.get(xshare_table); -- 将当前共享表的内容完整的复制到lua中,注意ret是一个正常table
示例:
local xt = xshare.new(‘test share table‘);
xt.d = {1,2,3};
local tab = xshare.get(xt);
local tab_d = xshare.get(xt.d);


xshare.set(xshare_table, table); -- 如果是要直接将根节点设置为一个表,可以使用这个方法,也就是它和xshare.get是反过来的作用
示例:
local xt = xshare.new(‘test share table‘);
xshare.set(xt, {d = {1,2,3}});


不能将一个xshare_table设置为nil,参数2:table必须是一个有效的table,可以是空表,但不能是nil,理由如下:
本来我已经写好了xshare.delete,最终我删掉了,原因是,由lua删除共享表根节点会把事情变得复杂,
因为根节点必定是所有线程一起保存在各自的文件中,其中某个线程删除了根节点,其他线程要解决错误就非常蛋疼了。
需要删除子节点可以使其=nil,而会被我们删除的子节点,本身就不应该由我们的来保存它的副本。
所以我仅仅考虑了在C++里释放内存的方式:lua_closexsharelib,当所有lua对象已经88的时候,由C++调用它即可释放内存
*/

int lua_openxsharelib(lua_State *s);
int lua_closexsharelib();
//lxsharelib.cpp
#include "pch.h"
#include <mutex>
#include <string>
#include <unordered_map>
#include <variant>
#include "lxsharelib.h"

class xshare_table;
using xshare_bool = unsigned char;
using xshare_value = std::variant<std::string, intptr_t, double, xshare_bool, xshare_table*>;
using xshare_key = std::variant<std::string, intptr_t, double>;
using xshare_type = std::unordered_map<xshare_key, xshare_value>;

class xshare_table {
public:
    xshare_table() {imax = 0;}
    ~xshare_table() {
        for (auto it = vars.begin(); it != vars.end(); ++it) {
            if (it->second.index() == 4) delete (std::get<4>(it->second));
        }
    }

    std::recursive_mutex mtx;
    intptr_t imax;
    xshare_type vars;
};

#define lua_isintnum(_Number_) (_Number_ - floor(_Number_) < 1e-6)


/*
 从lua栈上获取xshare_key
*/
int xshare_get_key(lua_State *s, int n, xshare_key &k) {

    int _Type = lua_type(s, n);
    switch (_Type) {
    case LUA_TNUMBER: {
        double dv = lua_tonumber(s, n);
        if (lua_isintnum(dv))//判断整数
            k = (intptr_t)dv;
        else
            k = dv;
        break;
    }
    case LUA_TSTRING:
        k = lua_tostring(s, n);
        break;
    default:
        // 莫名奇妙的key时,返回-1,让上层去报错
        return -1;
    }
    return 0;
}

/*
 从lua的栈上,获取xshare_table的指针
*/
xshare_table *xshare_ctab(lua_State *s) {
    if (!lua_gettop(s) || lua_type(s, 1) != LUA_TTABLE) {
        luaL_error(s, "invalid share_table");
        return nullptr;
    }

    lua_pushstring(s, "__ptr_");
    lua_gettable(s, 1);
    if (lua_type(s, -1) == LUA_TNIL) {
        // 获取 __ptr_失败时,报错
        lua_pop(s, 1);
        luaL_error(s, "invalid share_table");
        return nullptr;
    }
    xshare_table *_Result = (xshare_table *)lua_touserdata(s, -1);
    lua_pop(s, 1);
    return _Result;
}

/*
 将share_table中的key返回给lua的push环节
*/
void xshare_push_key(lua_State *s, const xshare_key &k) {
    switch (k.index()) {
    case 0://std::string
        lua_pushstring(s, std::get<0>(k).c_str());
        break;
    case 1://intptr_t
        lua_pushinteger(s, std::get<1>(k));
        break;
    case 2://double
        lua_pushnumber(s, std::get<2>(k));
        break;
    }
}


/*
 将share_table中的value返回给lua的push环节
*/
void xshare_push_val(lua_State *s, const xshare_value &v) {
    switch (v.index()) {
    case 0://std::string
        lua_pushstring(s, std::get<0>(v).c_str());
        break;
    case 1://intptr_t
        lua_pushinteger(s, std::get<1>(v));
        break;
    case 2://double
        lua_pushnumber(s, std::get<2>(v));
        break;
    case 3://xshare_bool(unsigned char)
        lua_pushboolean(s, std::get<3>(v));
        break;
    case 4://xshare_table*
        lua_newtable(s);
        lua_pushstring(s, "__ptr_");
        lua_pushlightuserdata(s, std::get<4>(v));
        lua_settable(s, -3);
        lua_getglobal(s, "__xshare_object_metatable");
        lua_setmetatable(s, -2);
        break;
    }
}

/*
 __index元方法
*/
int lua_xshare_get(lua_State *s) {
    auto p = xshare_ctab(s);
    xshare_key k;
    if(xshare_get_key(s, 2, k))
        luaL_error(s, "invalid xshare_key type:%s", lua_typename(s, lua_type(s, 2)));

    auto it = p->vars.find(k);
    if (it == p->vars.end())
        return 0;
    xshare_push_val(s, it->second);
    return 1;
}

int xshare_set_tab(lua_State *s, xshare_table *t, int n);

/*
 share_table.key = lua栈(n)
*/
int xshare_set_tabval(lua_State *s, xshare_table *t, xshare_key &key, int n) {

    auto it = t->vars.find(key);
    bool et = false;
    bool st = false;
    if (it != t->vars.end()) {
        if (it->second.index() == 4)
            st = true;
        et = true;
    }

    /*
     如果是由__newindex调用,LUA_TNIL已经在上层判断过了
     如果是由lua_next遍历table过来的调用,是不会获得nil的
     所以这里不需要判断nil类型
    */
    int _Type = lua_type(s, n);

    switch (_Type) {
    case LUA_TBOOLEAN:
        if (et) {
            //已存在的key
            if (st) delete (std::get<4>(it->second));//value是xshare_table时,删除它
            it->second = (xshare_bool)lua_toboolean(s, n);
        }
        else {
            
            t->vars[key] = (xshare_bool)lua_toboolean(s, n);
        }
        break;
    case LUA_TNUMBER: {
        double dv = lua_tonumber(s, n);
        if (et) {
            if (st) delete (std::get<4>(it->second));
            if (lua_isintnum(dv))
                it->second = (intptr_t)dv;
            else
                it->second = dv;
        }
        else
        {
            if (lua_isintnum(dv))
                t->vars[key] = (intptr_t)dv;
            else
                t->vars[key] = dv;
        }
        break;
    }
    case LUA_TSTRING: {
        if (et) {
            if (st) delete (std::get<4>(it->second));
            it->second = lua_tostring(s, n);
        }
        else {
            t->vars[key] = lua_tostring(s, n);
        }
        break;
    }
    case LUA_TTABLE: {
        xshare_table *_rt;
        if (et) {
            if (!st) {
                // 不是xshare_table时,预先创建它
                _rt = new xshare_table;
                it->second = _rt;
            }
            else {
                // 已经是xshare_table了,就直接返回它
                _rt = std::get<4>(it->second);
            }
        }
        else {
            _rt = new xshare_table;
            t->vars[key] = _rt;
        }
        
        return xshare_set_tab(s, _rt, ((n > 0) ? n : -2));//递归前进, n比0大时,直接传入n,否则直接传入-2(它包含了lua_pushnil,所以是-2)
        break;
    }
    default:
        return -1;
    }

    return 0;
}

int xshare_set_tab(lua_State *s, xshare_table *t, int n) {
    lua_pushnil(s);
    int _Result = 0;
    while (lua_next(s, n)) {
        xshare_key key;
        if (xshare_get_key(s, -2, key)) {
            //获取key错误时,清理栈并返回错误信息
            _Result = (1 << 16) | lua_type(s, -2);
            lua_pop(s, 1);
            break;
        }

        if (key.index() == 1) {
            //整数key时,记录最大index
            intptr_t i = std::get<1>(key);
            if (i > t->imax)
                t->imax = i;
        }


        int rc = xshare_set_tabval(s, t, key, -1);

        // 由此处判断rc的理由是,在产生错误之后,让最上层的调用处去报告错误,这样就能在出错之后最起码保证lua栈还是正确的
        if (rc == -1) {
            //设置数据错误时,如果返回值为-1,说明错误在当前这一层table里,此时应该清理栈并返回错误信息
            _Result = (2 << 16) | lua_type(s, -2);
            lua_pop(s, 1);
            break;
        }
        else {
            //如果返回值不为0,说明错误在由更下层的table返回,此时应该直接清理栈并返回rc
            if (rc != 0) {
                _Result = rc;
                lua_pop(s, 1);
                break;
            }
        }

        lua_pop(s, 1);
    }

    return 0;
}


/*
 __newindex元方法
*/
int lua_xshare_set(lua_State *s)
{
    auto p = xshare_ctab(s);
    xshare_key key;
    if(xshare_get_key(s, 2, key))
        luaL_error(s, "invalid xshare_key type:%s", lua_typename(s, lua_type(s, 2)));

    int vt = lua_type(s, 3);
    auto it = p->vars.find(key);
    if (it != p->vars.end()) {
        if (key.index() == 1) {
            intptr_t ikey = std::get<1>(it->first);
            if (vt == LUA_TNIL) {
                // 删除了最大的一个整数key时,让imax-1
                if (p->imax == ikey)
                    p->imax--;

                if (it->second.index() == 4) //如果被设置为nil的对象是一个xshare_table,删除它
                    delete (std::get<4>(it->second));
                p->vars.erase(it);
                return 0;
            }

            //被设置的ikey比imax更大时,更改记录
            if (ikey > p->imax)
                p->imax = ikey;
        }
        else
        {
            if (vt == LUA_TNIL) {
                if (it->second.index() == 4) //如果被设置为nil的对象是一个xshare_table,删除它
                    delete (std::get<4>(it->second));
                p->vars.erase(it);
                return 0;
            }
        }
    }
    else
    {
        // 当key不存在时,如果value是nil,直接返回
        // lua本身这个样子操作好像是要报错的,但这个报错我感觉意义并不大,也许这仅仅是我的感觉,如果要让它报错,删掉这个判断即可
        if (vt == LUA_TNIL) return 0;
        if (key.index() == 1) {
            intptr_t ikey = std::get<1>(key);
            if (ikey > p->imax)
                p->imax = ikey;
        }
    }

    int rc = xshare_set_tabval(s, p, key, 3);
    if (rc == -1) {
        luaL_error(s, "invalid xshare value type:%s", lua_typename(s, lua_type(s, 3)));
    }
    else {
        if (rc != 0) {
            luaL_error(s, 
                (((((unsigned int)rc) >> 16) & 0xFFFF) == 1) ? "invalid xshare_key type:%s" : "invalid xshare value type:%s", 
                lua_typename(s, (((unsigned int)rc) & 0xFFFF)));
        }
    }
    return 0;
}

/*
 给迭代器返回的next函数
*/
int lua_xshare_next(lua_State *s) {
    auto p = xshare_ctab(s);
    auto it = p->vars.end();
    
    if (lua_gettop(s) > 1 && lua_type(s, 2) != LUA_TNIL) {
        xshare_key key;
        if(xshare_get_key(s, 2, key))
            luaL_error(s, "invalid xshare_key type:%s", lua_typename(s, lua_type(s, 2)));
        it = p->vars.find(key);
    }

    if (it != p->vars.end()) 
        ++it;
    else
        it = p->vars.begin();
    
    if (it == p->vars.end())
        return 0;
    xshare_push_key(s, it->first);
    xshare_push_val(s, it->second);
    return 2;
}

/*
 __len元方法,也就是#tab
*/
int lua_xshart_getn(lua_State *s) {
    auto p = xshare_ctab(s);
    intptr_t j = p->imax;
    if (j > 0) {
        auto it = p->vars.find(j);
        if (it != p->vars.end())
        {
            lua_pushinteger(s, j);
            return 1;
        }

        //这一段二叉查找整数key边界,我是直接从lua5.3中的ltable.c中luaH_getn里抄过来的,
        //在5.4中它又有了新的变化,预先存在了一系列路径判断,尽可能的考虑不去用哈希表搜索,但最坏的情况还是存在这段代码,在binsearch函数中
        intptr_t i = 0;
        while (j - i > 1) {
            intptr_t m = (i + j) >> 1;
            if (p->vars.find(m) == p->vars.end())
                j = m;
            else
                i = m;
        }

        p->imax = i;
        lua_pushinteger(s, i);
        return 1;
    }
    lua_pushinteger(s, 0);
    return 1;
}

/*
 __pairs元方法
*/
int lua_xshare_pairs(lua_State *s)
{
    /*
      自定义pairs元方法的知识点,很少有人提及这个东西,我在这里顺带说一下:
        lua中的 for key, e in pairs(t) do ... end 是一串语法糖
        -- 实际上它等同于下面这样子
        local t, next, key = pairs(t);--第一个key必定是nil,就像我们在C里面lua_pushnil(s); while(lua_next(s, n))...一样
        while 1 do
            key, e = next(t, key);
            if key == nil then break; end--由next返回了key为nil,就说明结束了
            ...
        end
    */

    lua_pushcfunction(s, lua_xshare_next);
    lua_pushvalue(s, 1);
    lua_pushnil(s);
    return 3;
}

//所有共享数据都存在这里
xshare_table xtabs;


int lua_xshare_new(lua_State *s) {
    std::lock_guard<std::recursive_mutex> lg(xtabs.mtx);
    if (!lua_gettop(s)) 
        return 0;
    xshare_key key;
    if (xshare_get_key(s, 1, key))
        return 0;
    xshare_table *_Result = nullptr;
    auto it = xtabs.vars.find(key);
    if (it != xtabs.vars.end())
        _Result = std::get<4>(it->second);
    else {
        _Result = new xshare_table;
        xtabs.vars[key] = _Result;
    }

    lua_newtable(s);
    lua_pushstring(s, "__ptr_");
    lua_pushlightuserdata(s, _Result);
    lua_settable(s, -3);
    lua_getglobal(s, "__xshare_object_metatable");
    lua_setmetatable(s, -2);
    return 1;
}

int lua_xshare_lock(lua_State *s) {
    xshare_table *p = xshare_ctab(s);
    p->mtx.lock();
    return 0;
}

int lua_xshare_unlock(lua_State *s) {
    xshare_table *p = xshare_ctab(s);
    p->mtx.unlock();
    return 0;
}

int lua_xshare_mutex(lua_State *s) {
    xshare_table *p = xshare_ctab(s);
    p->mtx.lock();
    if (lua_gettop(s) < 2 && lua_type(s, 2) != LUA_TFUNCTION) {
        // 如果是调用xshare.mutex本身的参数错误了,那么就应该先解锁,然后报错
        // 因为luaL_error会longjmp到上一个lua_pcall里
        p->mtx.unlock();
        luaL_error(s, "xshare.mutex args error, should xshare.mutex(share_table, func)");
        return 0;
    }

    lua_pushvalue(s, 2);
    lua_pushvalue(s, 1);
    if (lua_pcall(s, 1, 1, 0)) {
        printf("xshare_mutex->lua_pcall error:%s\n", lua_tostring(s, -1));
        lua_pop(s, 1);//pcall错误时,先输出错误信息,然后将栈弹出
        p->mtx.unlock();
        return 0;
    }

    p->mtx.unlock();
    //正常完成之后,返回一个回调函数的返回值
    return 1;
}

void xshare_completeget_push(lua_State *s, xshare_table *p) {
    for (auto it = p->vars.begin(); it != p->vars.end(); ++it) {
        xshare_push_key(s, it->first);
        auto &v = it->second;
        switch (v.index()) {
        case 0://std::string
            lua_pushstring(s, std::get<0>(v).c_str());
            break;
        case 1://intptr_t
            lua_pushinteger(s, std::get<1>(v));
            break;
        case 2://double
            lua_pushnumber(s, std::get<2>(v));
            break;
        case 3://xshare_bool(unsigned char)
            lua_pushboolean(s, std::get<3>(v));
            break;
        case 4://xshare_table*
            lua_newtable(s);
            xshare_completeget_push(s, std::get<4>(v));
            break;
        }
        lua_settable(s, -3);
    }
}

int lua_xshare_completeget(lua_State *s) {
    xshare_table *p = xshare_ctab(s);
    lua_newtable(s);
    xshare_completeget_push(s, p);
    return 1;
}


int lua_xshare_completeset(lua_State *s) {
    xshare_table *p = xshare_ctab(s);
    if (lua_gettop(s) < 2 || lua_type(s, 2) != LUA_TTABLE) 
        luaL_error(s, "xshare.set args error, should xshare.set(share_table, table)");

    for (auto it = p->vars.begin(); it != p->vars.end(); ++it) {
        if (it->second.index() == 4)
            delete (std::get<4>(it->second));
    }
    p->vars.clear();
    xshare_set_tab(s, p, 2);
    return 0;
}

int lua_openxsharelib(lua_State *s) {
    lua_newtable(s);
    lua_pushcfunction(s, lua_xshare_get);
    lua_setfield(s, -2, "__index");

    lua_pushcfunction(s, lua_xshare_set);
    lua_setfield(s, -2, "__newindex");

    lua_pushcfunction(s, lua_xshare_pairs);
    lua_setfield(s, -2, "__pairs");

    lua_pushcfunction(s, lua_xshart_getn);
    lua_setfield(s, -2, "__len");

    lua_setglobal(s, "__xshare_object_metatable");


    lua_newtable(s);

    lua_pushcfunction(s, lua_xshare_new);
    lua_setfield(s, -2, "new");

    lua_pushcfunction(s, lua_xshare_lock);
    lua_setfield(s, -2, "lock");

    lua_pushcfunction(s, lua_xshare_unlock);
    lua_setfield(s, -2, "unlock");

    lua_pushcfunction(s, lua_xshare_mutex);
    lua_setfield(s, -2, "mutex");

    lua_pushcfunction(s, lua_xshare_completeget);
    lua_setfield(s, -2, "get");

    lua_pushcfunction(s, lua_xshare_completeset);
    lua_setfield(s, -2, "set");
    
    lua_setglobal(s, "xshare");

    return 0;
}

int lua_closexsharelib() {
    std::lock_guard<std::recursive_mutex> lg(xtabs.mtx);
    for (auto it = xtabs.vars.begin(); it != xtabs.vars.end(); ++it) {
        if (it->second.index() == 4)
            delete (std::get<4>(it->second));
    }
    xtabs.vars.clear();
}

 

 

// ConsoleApplication4.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include "pch.h"
#include <thread>
#include <iostream>
#pragma comment(lib, "lua54.lib")
#include "lxsharelib.h"
int main(int argc, char **argv)
{

    auto t1 = std::thread([]() {
        lua_State *s1 = luaL_newstate();
        luaL_openlibs(s1);
        lua_openxsharelib(s1);
        if (luaL_dofile(s1, "G:\\vs2017\\ConsoleApplication2\\x64\\Release\\script\\xshare.lua"))
            printf("%s\n", lua_tostring(s1, -1));
        lua_close(s1);
    });

    //这里的sleep,是为了让上面那个线程先跑一会儿,因为本例中对共享table的数据写入,是由它完成的。。
    std::this_thread::sleep_for(std::chrono::microseconds(10));
    
    //下边这个线程只是测试输出一下,具体可以看lua代码
    auto t2 = std::thread([]() {
        lua_State *s2 = luaL_newstate();
        luaL_openlibs(s2);
        lua_openxsharelib(s2);
        luaL_dofile(s2, "G:\\vs2017\\ConsoleApplication2\\x64\\Release\\script\\xshare2.lua");
        lua_close(s2);
    });

    t1.join();
    t2.join();

    return 0;
}

 

测试脚本代码:

-- xshare.lua
-- print附近的注释为预测输出的内容
local xt = xshare.new(test share table)


xshare.lock(xt);
print(******************s1******************);
xt.a = text;
print(xt.a);--text
xt.b = 111222333;
print(xt.b);--1122333
xt.c = true;
print(xt.c)--true
xt.c = false;
print(xt.c)--false

xt.d = {1,2,3};

--[[
    预期的输出:
    1
    2
    3
]]
for i, e in ipairs(xt.d) do
    print(e);
end


xt.d[4] = 4;

--[[
    预期的输出:
    1
    2
    3
    4
]]
for i, e in ipairs(xt.d) do
    print(e);
end

xt.e = {aa=1t, bb=2, cc=true};

--[[
    
    要注意:hash表遍历是不能保证顺序的
    预期的输出:
    aa  1t
    bb  2
    cc  true
]]
for i, e in pairs(xt.e) do
    print(i, e)
end


xt.f = {[11]=11,1,2,3,nil,5,6,7,8,9,10,x=12};

print(#(xt.f))-- 11
xt.f[10] = nil;
print(#(xt.f))-- 11
xt.f[11] = nil;
print(#(xt.f))-- 9
xt.f[9] = nil;
print(#(xt.f));-- 8


--[[
预期的输出:
1:1
x:12
2:2
3:3
5:5
6:6
7:7
8:8
]]
xshare.mutex(xt.f, function(_Tab)
    for i, e in pairs(_Tab) do
        print(i..:..e);
    end
end);


-- 使用xshare.mutex主要的目的是避免死锁问题
-- 下面我们来实现一个错误
xshare.mutex(xt, function(_Tab)
    _Tab.g = function() print(1) end;
end);


-- 报错之后,继续执行

xshare.mutex(xt, function(_Tab)

    print(test get set);
    -- 完整获取原来的table
    local old = xshare.get(_Tab);
    
    -- 将它设置为一个新的table
    xshare.set(_Tab, {x = 1, y = 2, z = 3});

    --[[
        预测的输出:
        x:1
        y:2
        z:3
    ]]
    for i, e in pairs(_Tab) do
        print(i..:..e);
    end

    -- 还原它
    xshare.set(_Tab, old);
end)


xshare.unlock(xt);

 

 

-- xshare2.lua
-- print附近的注释为预测输出的内容
local xt = xshare.new(test share table)


xshare.lock(xt);
print(******************s2******************);
print(xt.a);--text

print(xt.b);--1122333

print(xt.c)--true


--[[
    1
    2
    3
    4
]]
for i, e in ipairs(xt.d) do
    print(e);
end

--[[
    要注意:hash表遍历是不能保证顺序的

    aa  1t
    bb  2
    cc  true
]]
for i, e in pairs(xt.e) do
    print(i, e)
end
xshare.unlock(xt);

 

******************s1******************

text

111222333

true

false

1

2

3

1

2

3

4

bb      2

cc      true

aa      1t

11

11

9

8

1:1

x:12

2:2

3:3

5:5

6:6

7:7

8:8

xshare_mutex->lua_pcall error:G:\vs2017\ConsoleApplication2\x64\Release\script\xshare.lua:90: invalid xshare value type:function

test get set

x:1

z:3

y:2

******************s2******************

text

111222333

false

1

2

3

4

bb      2

cc      true

aa      1t

相关文章
相关标签/搜索