最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

C++與Lua協(xié)程交互的示例詳解

 更新時間:2024年02月10日 09:10:58   作者:江澎涌  
Lua 語言不支持真正的多線程,即不支持共享內存的搶占式線程,在執(zhí)行協(xié)程體的 Lua 腳本時,Lua 同樣可以調用 C++ 的函數(shù),本文給大家介紹了C++ 與 Lua 的協(xié)程交互,需要的朋友可以參考下

零、前言

Lua 語言不支持真正的多線程,即不支持共享內存的搶占式線程。

這樣的模式能減少一些多線程的問題。多線程的問題源于線程搶占和共享內存,而如果非搶占式線程或者不使用共享內存則能避免多線程問題,Lua 同時支持這兩種方案。從之前分享的《Lua 協(xié)程》文章中知道:

  • Lua 語言的線程是協(xié)作式的,即協(xié)程,可以避免因不可預知的線程切換帶來的問題。
  • Lua 狀態(tài)間內存不共享,所以各個狀態(tài)相互獨立運行,可以并行操作。

一、多線程

從 C-API 的角度,可以把線程當作一個棧,每個棧保存著一個線程中掛起的函數(shù)調用信息,以及每個函數(shù)調用的參數(shù)和局部變量。也就是說,一個棧包括了一個線程得以繼續(xù)運行所需的所有信息。 因此,要達到多線程就需要多個棧。

每當創(chuàng)建一個 lua_State 時,Lua 就會自動用這個 lua_State 創(chuàng)建一個主線程。這個主線程永遠不會被垃圾回收,只有當調用 lua_close 關閉時才會釋放。

可以通過 C-API lua_newthread 在已有的 lua_State 中創(chuàng)建新的線程。

二、lua_newthread

lua_State *(lua_newthread) (lua_State *L);

描述:

這個函數(shù)會將新線程 thread 類型的值壓入棧中,并返回一個表示該線程的 lua_State 類型的指針。

新線程和舊線程的異同點:

可以使用以下代碼創(chuàng)建了一個新的線程,并且得到一個新的 lua_State 指針類型的 L1 值。

lua_State *L1 = lua_newthread(L);
  • 新線程 L1 與主線程 L 共享全局變量和注冊表,但是它們具有獨立的??臻g。新線程 L1 從空棧開始,主線程 L 則在其棧頂會引用 L1 這個新線程。
  • 除了主線程 L 需要使用 lua_close 進行關閉,新線程 L1 和其他的 Lua 對象一樣都是垃圾回收的對象。所以永遠不要使用未被正確錨定在 lua_State 中的線程(主線程是內部錨定,因此不用擔心會被回收),因為所有對 Lua API 的調用都有可能回收未錨定的線程,即使是正在使用這個線程進行函數(shù)調用。 要避免這種情況,應該在 “已錨定的線程的棧”、“注冊表”、“Lua 變量” 中保留對使用中線程的引用,防止被垃圾回收機制回收。
  • 擁有的新線程 L1 可以像主線程 L 一樣使用,進行壓棧調用函數(shù)等操作。

舉個例子:

  • 創(chuàng)建一個新的線程。
  • 分別打印各自棧的內容。
lua_State *L = luaL_newstate();
luaL_openlibs(L);

// 用 L 創(chuàng)建一個新的線程
// L 和 L1 各自有一個棧
// L 的棧頂是 L1 的 thread
// L1 的棧是空
lua_State *L1 = lua_newthread(L);

printf("主線程 L 棧深度:%d\n", lua_gettop(L));
printf("主線程 L 棧內容:------------\n");
stackDump(L);

printf("新線程 L1 棧深度:%d\n", lua_gettop(L1));
printf("新線程 L1 棧內容:------------\n");
stackDump(L1);

lua_close(L);

輸出如下:

主線程 L 棧深度:1
------------ 主線程 L 棧內容:------------
棧頂
^ typename: thread, value: thread    
棧底

新線程 L1 棧深度:0
------------ 新線程 L1 棧內容:------------
棧頂
棧底

三、新線程的作用

如果創(chuàng)建一個新的線程,只是用來運行簡單的函數(shù),這種場景其實只需要在主線程中執(zhí)行即可。

創(chuàng)建新線程的主要目的是運行協(xié)程。 可以在新線程中掛起協(xié)程,然后繼續(xù)運行其他線程的 Lua 代碼,在需要的節(jié)點恢復協(xié)程,使新線程繼續(xù)執(zhí)行掛起點之后的邏輯代碼,而運行協(xié)程需要用到 lua_resume C-API 。

四、lua_resume

LUA_API int  (lua_resume)     (lua_State *L, lua_State *from, int narg,
                               int *nres);

描述:

使用 lua_resume 和使用 lua_pcall 調用函數(shù)很相似,壓入?yún)f(xié)程的參數(shù),然后將待調用函數(shù)(協(xié)程體)壓入棧,并以參數(shù)的數(shù)量作為參數(shù) narg 調用 lua_resume

lua_resumelua_pcall 的不同點:

  • lua_resume 中沒有表示期望結果數(shù)量的參數(shù),總是返回被調用函數(shù)的所有結果。
  • 沒有錯誤處理函數(shù)的參數(shù),發(fā)生錯誤時不會進行棧展開,可以在錯誤發(fā)生后檢查棧的情況。
  • 如果正在運行的函數(shù)被掛起,lua_resume 就會返回代碼 LUA_YIELD ,并將線程置于一個后續(xù)可以恢復執(zhí)行的狀態(tài)中。

參數(shù):

  • 參數(shù) L:Lua State 的指針。
  • 參數(shù) from:正在執(zhí)行調用的線程,或為 NULL 。
  • 參數(shù) narg:入?yún)?shù)數(shù)量。
  • 參數(shù) nres:協(xié)程體返回的值數(shù)量。

返回值:

返回當前協(xié)程體處于哪個狀態(tài):

  • 如果被掛起,則返回 LUA_YIELD 。
  • 如果已經(jīng)執(zhí)行完成,則返回 LUA_OK 。
  • 如果 Lua 腳本中拋出異常,則會返回 LUA_ERRxxx 類型的錯誤。

值傳遞:

lua_Stack 的棧是相互獨立,所以此時如果需要將協(xié)程產(chǎn)出的數(shù)據(jù)傳遞到另一個 lua_State 中。

  • 可以將需要的數(shù)據(jù)通過 C-API 獲取至 C 中,處理之后,再壓入另一個 lua_State 的棧中。
  • 可以使用 lua_xmove 將數(shù)據(jù)傳遞至另一個 lua_State 中。

Lua 調用 C++ 時,C++ 函數(shù)掛起協(xié)程體:

在執(zhí)行協(xié)程體的 Lua 腳本時,Lua 同樣可以調用 C++ 的函數(shù)。C++ 函數(shù)體內也可以進行協(xié)程掛起,通過 lua_yieldk 便可以進行達到協(xié)程掛起。

五、lua_yieldk

LUA_API int  (lua_yieldk)     (lua_State *L, int nresults, lua_KContext ctx,
                               lua_KFunction k);

描述:

將正在運行的協(xié)程體立即掛起。

當協(xié)程恢復運行時,控制權會直接交給延續(xù)函數(shù) k 。當協(xié)程交出控制后,調用 lua_yield 的函數(shù)后續(xù)語句就不會被執(zhí)行了。

參數(shù):

  • 參數(shù) L:Lua State 的指針。
  • 參數(shù) nresults:將要返回給對應的 lua_resume 的棧中值的個數(shù)。
  • 參數(shù) ctx:傳遞給延續(xù)的上下文信息。
  • 參數(shù) k:延續(xù)函數(shù)。

值得注意:

如果 C++ 函數(shù)交出控制權之后,無需做后續(xù)處理,即掛起再恢復后,無需執(zhí)行后續(xù)操作,可以不設置參數(shù) k (設置為 NULL)。具體代碼如下所示:

#define lua_yield(L,n)		lua_yieldk(L, (n), 0, NULL)

六、C++ 與 Lua 協(xié)程交互的例子

  • 創(chuàng)建一個新線程 lua_State ,在這個新線程中執(zhí)行協(xié)程體。
  • 協(xié)程體內,首先會在 Lua 腳本中掛起,并返回數(shù)據(jù)到宿主中。
  • 恢復協(xié)程體后,Lua 從掛起點繼續(xù)運行,調用 C++ 函數(shù),C++ 函數(shù)內掛起協(xié)程體,并返回數(shù)據(jù)到宿主。
  • 再次恢復協(xié)程后,運行 C++ 函數(shù)掛起點的延續(xù)函數(shù),運行完延續(xù)函數(shù),回至 Lua 調用 C++ 調用點繼續(xù)執(zhí)行至結束,返回數(shù)據(jù)到宿主。

第一步,編寫 C++ 函數(shù),暴露給 Lua 調用。

// 延續(xù)函數(shù)
int cfooK(lua_State *L, int status, lua_KContext ctx) {
    printf("恢復協(xié)程體后,調用 C++ 延續(xù)函數(shù)\n");
    return 1;
}

// 暴露給 lua 調用的 C++ 函數(shù)
int primCFunction(lua_State *L) {
    lua_pushstring(L, "調用 C++ 函數(shù),會進行掛起");

    // 設置了 cfooK 作為延續(xù)函數(shù),恢復協(xié)程之后,會進入 cfook 這一延續(xù)函數(shù)
    lua_yieldk(L, 1, 0, &cfooK);

    // 這兩種的使用結果是一樣的,都不舍之延續(xù)函數(shù)
//    lua_yieldk(L, 1, 0, nullptr);
//    lua_yield(L, 1);

    // 不會被執(zhí)行,恢復之后會運行延續(xù)函數(shù),不會執(zhí)行后續(xù)的語句
    printf("掛起之后的輸(不會被輸出)");

    return 1;
}

第二步,編寫 Lua 腳本文件。

function foo(x)
    -- 協(xié)程掛起,并返回兩個數(shù)據(jù)
    coroutine.yield(10, x)
end

function foo1(x)
    foo(x + 1)

    primCFunction()

    return 3
end

第三步,最后編寫運行代碼。

void useThread() {
    lua_State *L = luaL_newstate();
    luaL_openlibs(L);
    lua_State *L1 = lua_newthread(L);

    lua_pushcfunction(L1, primCFunction);
    lua_setglobal(L1, "primCFunction");

    std::string fname = PROJECT_PATH + "/12、線程和狀態(tài)/thread/coroutine.lua";
    if (luaL_loadfile(L1, fname.c_str()) || lua_pcall(L1, 0, 0, 0)) {
        printf("can't run config. file: %s", lua_tostring(L1, -1));
    }

    printf("LUA_YIELD=%d\n", LUA_YIELD);
    printf("LUA_OK=%d\n", LUA_OK);

    // 返回值個數(shù)
    int result = 0;
    // 獲取 L1 中的 lua 文件的函數(shù) foo1
    lua_getglobal(L1, "foo1");
    // 壓入 integer
    lua_pushinteger(L1, 20);

    printf("第一次調用,Lua 腳本中掛起:\n");
    auto state = lua_resume(L1, L, 1, &result);
    printf("協(xié)程狀態(tài): %s\n", getResumeState(state).c_str());
    printf("L1 棧深度: %d\n", lua_gettop(L1));
    printf("返回值個數(shù): %d\n", result);
    printf("------------ L1 棧內容:------------\n");
    stackDump(L1);

    printf("第二次調用,Lua 調用 C++ ,C++ 中掛起:\n");
    state = lua_resume(L1, L, 0, &result);
    printf("協(xié)程狀態(tài): %s\n", getResumeState(state).c_str());
    printf("L1 棧深度: %d\n", lua_gettop(L1));
    printf("返回值個數(shù): %d\n", result);
    printf("------------ L1 棧內容:------------\n");
    stackDump(L1);

    printf("第三次調用,運行 C++ 延續(xù)函數(shù),協(xié)程體結束::\n");
    state = lua_resume(L1, L, 0, &result);
    printf("協(xié)程狀態(tài): %s\n", getResumeState(state).c_str());
    printf("L1 棧深度: %d\n", lua_gettop(L1));
    printf("返回值個數(shù): %d\n", result);
    printf("------------ L1 棧內容:------------\n");
    stackDump(L1);

    lua_close(L);
}

最后運行的結果如下,這里就不再一個個步驟拆解了,代碼有詳細的注釋,結合運行的結果便可分析出流轉的過程了。

LUA_YIELD=1
LUA_OK=0
第一次調用,Lua 腳本中掛起:
協(xié)程狀態(tài): LUA_YIELD
L1 棧深度: 2
返回值個數(shù): 2
------------ L1 棧內容:------------
棧頂
^ typename: number, value(integer): 21    
^ typename: number, value(integer): 10    
棧底

第二次調用,Lua 調用 C++ ,C++ 中掛起:
協(xié)程狀態(tài): LUA_YIELD
L1 棧深度: 1
返回值個數(shù): 1
------------ L1 棧內容:------------
棧頂
^ typename: string, value: '調用 C++ 函數(shù),會進行掛起'    
棧底

第三次調用,運行 C++ 延續(xù)函數(shù),協(xié)程體結束::
協(xié)程狀態(tài): LUA_OK
L1 棧深度: 1
返回值個數(shù): 1
------------ L1 棧內容:------------
棧頂
^ typename: number, value(integer): 3    
棧底

七、lua_xmove

LUA_API void  (lua_xmove) (lua_State *from, lua_State *to, int n);

描述:

交換同一個狀態(tài)機下不同線程中的值,會從 from 的棧上彈出 n 個值, 然后把它們壓入 to 的棧上。

舉個例子:

  • 創(chuàng)建一個 lua_State "L",然后從這一 lua_State 創(chuàng)建一個新的線程 lua_State "L1"。
  • 通過 L1 執(zhí)行 Lua 腳本,得到兩個數(shù)據(jù)。
  • 將 L1 的數(shù)據(jù)移動到 L 中,打印結果。

第一步,編寫 lua 腳本。

function foo(x)
    return "江澎涌", x * x
end

第二步,編寫主函數(shù)。

void copyStackElement() {
    lua_State *L = luaL_newstate();
    luaL_openlibs(L);
    lua_State *L1 = lua_newthread(L);

    // 新線程中執(zhí)行 Lua 腳本
    std::string fname = PROJECT_PATH + "/12、線程和狀態(tài)/thread/copy_stack.lua";
    if (luaL_loadfile(L1, fname.c_str()) || lua_pcall(L1, 0, 0, 0)) {
        printf("can't run config. file: %s", lua_tostring(L1, -1));
    }

    // 獲取 L1 中的 lua 文件的函數(shù) foo
    lua_getglobal(L1, "foo");
    // 壓入 integer
    lua_pushinteger(L1, 5);
    // 調用函數(shù) foo(5) -> "江澎涌", 25
    lua_call(L1, 1, 2);

    printf("------------ 主線程 L 棧內容(xmove 前):--------------\n");
    stackDump(L);

    printf("------------ 新線程 L1 棧內容(xmove 前):--------------\n");
    stackDump(L1);
    // 從 L1 中拷貝 1 個元素到 L 中(會將 L1 元素彈出)
    lua_xmove(L1, L, 2);

    printf("------------ 主線程 L 棧內容(xmove 后):--------------\n");
    stackDump(L);

    printf("------------ 新線程 L1 棧內容(xmove 后):--------------\n");
    stackDump(L1);
}

八、寫在最后

Lua 項目地址:Github傳送門

以上就是C++與Lua協(xié)程交互的示例詳解的詳細內容,更多關于C++與Lua協(xié)程交互的資料請關注腳本之家其它相關文章!

相關文章

  • C/C++中的名字空間與作用域示例詳解

    C/C++中的名字空間與作用域示例詳解

    這篇文章主要給大家介紹了關于C/C++中名字空間與作用域的相關資料,文中通過示例代碼介紹的非常詳細,對大家學習或者使用c/c++具有一定的參考學習價值,需要的朋友們下面隨著小編來一起看看吧。
    2017-09-09
  • Qt處理焦點事件(獲得焦點,失去焦點)

    Qt處理焦點事件(獲得焦點,失去焦點)

    本文主要介紹了Qt處理焦點事件(獲得焦點,失去焦點),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2023-12-12
  • 最大對稱字符串的算法

    最大對稱字符串的算法

    題目:輸入一個字符串,輸出該字符串中對稱的子字符串的最大長度。比如輸入字符串“google”,由于該字符串里最長的對稱子字符串是“goog”,因此輸出4。
    2013-03-03
  • C語言中getchar的用法以及實例解析

    C語言中getchar的用法以及實例解析

    getchar()是stdio.h中的庫函數(shù),它的作用是從stdin流中讀入一個字符,下面這篇文章主要給大家介紹了關于C語言中getchar的用法以及實例的相關資料,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下
    2022-03-03
  • C語言在linux下編程詳解

    C語言在linux下編程詳解

    這篇文章主要介紹了linux下基于C語言的編程,實例分析了基本使用技巧與相關概念,具有一定參考借鑒價值,需要的朋友可以參考下
    2021-08-08
  • Dev C++ 安裝及使用方法(圖文教程)

    Dev C++ 安裝及使用方法(圖文教程)

    Dev C++ 是一款非常好用,簡約的C/C++開發(fā)工具,本文主要介紹了Dev C++ 安裝及使用方法(圖文教程),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2022-06-06
  • C語言自動生成enum值和名字映射代碼

    C語言自動生成enum值和名字映射代碼

    這篇文章主要介紹了C語言自動生成enum值和名字映射代碼的相關資料,需要的朋友可以參考下
    2015-12-12
  • c++中vector<int>和vector<int*>的用法及區(qū)別

    c++中vector<int>和vector<int*>的用法及區(qū)別

    這篇文章主要介紹了c++中vector<int>和vector<int*>的用法及區(qū)別,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2013-10-10
  • C語言實現(xiàn)交換排序算法(冒泡,快速排序)的示例代碼

    C語言實現(xiàn)交換排序算法(冒泡,快速排序)的示例代碼

    這篇文章主要為大家詳細介紹了如何利用C語言實現(xiàn)交換排序算法(冒泡排序、快速排序),文中的示例代碼講解詳細,感興趣的小伙伴可以了解一下
    2022-07-07
  • C++實現(xiàn)廣度優(yōu)先搜索實例

    C++實現(xiàn)廣度優(yōu)先搜索實例

    這篇文章主要介紹了C++實現(xiàn)廣度優(yōu)先搜索,對于C++程序員來說非常有借鑒價值,需要的朋友可以參考下
    2014-08-08

最新評論

玉山县| 手游| 新密市| 灵丘县| 黄骅市| 嵩明县| 双城市| 永登县| 普洱| 文水县| 阳西县| 金华市| 屯门区| 莱西市| 邻水| 南华县| 南安市| 抚顺县| 四子王旗| 广安市| 小金县| 陇西县| 通道| 凌海市| 安岳县| 泗水县| 绥宁县| 当涂县| 定襄县| 潮州市| 邢台市| 红安县| 蓬莱市| 综艺| 伊宁市| 谢通门县| 滦南县| 南城县| 于都县| 金塔县| 东平县|