从C中更改lua变量

我有一个主程序(用C编写),它需要分支到lua_thread(主程序继续运行)。这个lua_thread调用一个lua_script.lua。这个lua_script包含一个while循环。一个lua变量控制这个while循环。目前这个循环永远不会停止运行。

lua_script.lua

  --this loop runs forever, as the exit value is not set yet
  a=0
    while(a<=0)
    do
       print("value of a:", a)
    end

我的目标是从主程序更改这个lua变量(a),使其退出这个无限循环。一旦循环结束,它就会退出线程并返回到主程序。

main.c

#include <lua.h>
#include <lauxlib.h>
#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
void *lua_thread()
 {
    int status, result;
    double sum;
    lua_State *L;

    L = luaL_newstate();
    luaL_openlibs(L);

    status = luaL_loadfile(L, "lua_script.lua");
    if (status)
    {
        fprintf(stderr, "Couldn't load file: %s\n", lua_tostring(L, -1));
        exit(1);
    }

result = lua_pcall(L, 0, 0, 0);
    if (result) {
        fprintf(stderr, "Failed to run script: %s\n", lua_tostring(L, -1));
        exit(1);
    }

    lua_close(L);
    return 0;
}

int main(void)
{
    pthread_t p1;
    pthread_create(&p1,NULL,lua_thread,NULL);
    pthread_join(p1,NULL);
    return 0;
}

如果你运行上面的代码

cc -o xcute main.c  -I/usr/include/lua5.2 -llua -lm -ldl -pthread

它将进入一个无限循环。我想以某种方式控制lua变量并将其更改为a = 1,从主程序中使其退出无限循环。进行这样的测试的原因是,它将确保在主程序退出之前,通过控制lua变量,先退出此线程。

请建议如何更改这个lua变量,使其退出while循环。

点赞
用户258523
用户258523

从不同的线程与运行中的lua状态交互不一定安全

因此,根据您计划从C端进行更改的位置,修改脚本的全局变量可能是一个好主意,也可能不是。

如果要这样做,您只需要使用lua C api在适当的lua状态中设置适当名称的全局变量即可。

另一种想法是创建一个should_exit全局函数,在每个循环的开头或结尾调用它,当它返回true时,会导致lua代码“break”或“return”。然后,该函数可以以线程适当的方式在C端检查任何所需的内容。

2015-06-24 12:04:44
用户3125367
用户3125367

为什么要在 Lua 中循环?你可以使用 c-thread 循环,每次迭代都通过 lua_pcall 调用某个入口函数(例如 onEvent())。

如果必须在 Lua 脚本中使用循环,例如在设置-循环-清理结构的情况下,可以在协程中运行脚本并使用 coroutine.yield() 作为循环条件。线程应该使用 true 值或退出的 lua_resume(),具体取决于你的 c-side 条件。(或者如果首选循环后 Lua-side 清理的话,则使用 false 再次恢复。)

无论如何,Lua 不支持多线程,不能同时从多个线程调用。

2015-06-24 17:34:28
用户4374077
用户4374077

Lua 线程中使用信号量退出

main.c

#include <lua.h>
#include <lauxlib.h>
#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#include <assert.h>
#include <fcntl.h>
#include <semaphore.h>

void *lua_thread(void *arg)
{
    int status, result, i;
    double sum;
    lua_State *L = (lua_State *)arg;

    int a = 0;
    status = luaL_dofile(L, "lua_script.lua");
    if (status) {
        fprintf(stderr, "Couldn't load file: %s\n", lua_tostring(L, -1));
        exit(1);
    }

    printf("Lua thread exiting\n");
    return 0;
}

int main(void)
{
    lua_State *L;
    L = luaL_newstate();
    luaL_openlibs(L);

    // 打开信号量,该信号量将会通知 Lua 循环退出
    sem_t *lex = sem_open("luaexitsem", O_CREAT, 0600, 0);

    // 在开始运行之前,确保信号量的值为 0,
    // 这样 sem_post 就会将它设置为 1
    int retval;
    for (retval = 0; retval == 0;) {
        retval = sem_trywait(lex);
        printf("Dec sem val: %d\n", retval);
    }

    // 启动 Lua 线程
    pthread_t p1;
    pthread_create(&p1, NULL, lua_thread, L);

    sleep(5);

    // 发出信号告知 Lua 脚本退出
    sem_post(lex);

    // 等待 Lua 线程退出
    pthread_join(p1, NULL);

    // 清理
    printf("Main exiting\n");
    lua_close(L);
    sem_close(lex);
    return 0;
}

编译命令

gcc -o main main.c -I/usr/include/lua5.1 -llua5.1 -lm -ldl -pthread

lua_script.lua

require "lualinuxthread"

a = 1
while (not linuxthread.signaltoexit()) do
    print("value of a:", a)
    a = a + 1
end

lualinuxthread.c

#define LUA_LIB
#include "lua.h"
#include "lauxlib.h"
#include <semaphore.h>
#include <errno.h>
#include <fcntl.h>

static int signaltoexit(lua_State *L)
{

    sem_t *lex = sem_open("luaexitsem", O_CREAT, 0600, 0);

    int exitvalue = 0, retval;

    if (lex != SEM_FAILED)
        retval = sem_trywait(lex);

    if (retval == -1)
        exitvalue = 0;
    else
        exitvalue = 1;

    printf("signaltoexit - exitvalue: %d, retval: %d,  %x\n", exitvalue, retval, lex);

    lua_pushboolean(L, exitvalue);

    sem_close(lex);
    return 1;
}

static const luaL_Reg libfuncs[] = {
    {"signaltoexit", signaltoexit},
    {NULL, NULL}};

LUALIB_API int luaopen_lualinuxthread(lua_State *L)
{

    luaL_register(L, "linuxthread", libfuncs);
    return 1;
}

编译命令

gcc -O -O2 -fpic -shared lualinuxthread.c -o lualinuxthread.so -lpthread -llua5.1

感谢!

2015-06-26 11:10:34
用户3125367
用户3125367

哦,你在回答中走了一条艰难的道路(当然这也是一个很好的练习)。事情可能要简单得多:

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>

static volatile int shouldRun = 1; // 1.

static int
fn_sleep(lua_State *L)
{
  lua_Integer n = luaL_checkinteger(L, 1);
  sleep(n < 0 ? 0 : n); 
  return 0;
}

static int
fn_shouldRun(lua_State *L)
{
  lua_pushboolean(L, shouldRun);
  return 1;
}

static void *
thread_main(void *dummy)
{
  lua_State *L = luaL_newstate();
  luaL_openlibs(L);

  lua_register(L, "sleep", fn_sleep); // 2.
  lua_register(L, "shouldRun", fn_shouldRun);

  if (luaL_dofile(L, "script.lua")) {
    fprintf(stderr, "%s\n", lua_tostring(L, -1));
    lua_pop(L, 1);
  }

  lua_close(L); // 3.
  return 0;
}

int
main(int argc, char *argv[])
{
  pthread_t thread;
  pthread_create(&thread, NULL, thread_main, NULL);

  sleep(5);
  shouldRun = 0; // 1.

  pthread_join(thread, NULL);
  return 0;
}

script.lua:

print("Hi!")

while shouldRun() do
  print("Running!")
  sleep(1)
end

print("Bye!")

output:

Hi!
Running!
Running!
Running!
Running!
Running!
Bye!

需要注意几点:

  1. 轮询的退出条件通常不需要任何保护措施。如果shouldRun()同时与变量更新被调用并且错过了它,那么无需担心-它将在下一次返回 false。无论如何,你甚至无法猜测脚本在哪里执行,只需要重置该值并等待完成。
  2. 对于一对特定的函数,无需编写与require/preload兼容的模块。将它们导出到全局命名空间中。
  3. 对于优雅的错误关闭,不要仅使用 exit() lua_close()将收集脚本使用的所有UserData,它们的 __gc元方法可能会做一些有用的事情,如将缓冲区刷新到磁盘等。硬退出会失去所有机会。
2015-06-26 16:44:42