在lua脚本调用中,如果我们碰到一种不好的脚本,例如:

 

while 1 do

     do

end

 

那我们的程序主线程也会被阻塞住。那我们如何防止这种问题呢?下面就给出一个解决的办法。

 

首先为了不阻塞主线程,那我们就要开一个线程,把处理脚本的操作都放在这个新开的工作线程里。(要详细了解工作线程和界面线程的区别和管理,请参看本人的另一篇文章BLOG下Windows编程里的《Windows 线程漫谈——界面线程和工作者线程》)。

 

总体思路:

1、开线程来执行脚本解析,下面是StartRun()

2、导出一个判断是否结束函数,让脚本每个循环都调用,判断线程是否该结束了,这个函数在下面是IsThreadExit(),返回一个字符串("exit" 表示while循环该结束了,"Notexit" 表示线程还不能结束)

3、如果主线程需要主动结束线程,就调用StopRun()

4、最好导出一个 ySleep 停顿函数,以免while循环里执行太快,导致CPU被高占用。http://hovertree.com/

 

 

按照这种思路,lua脚本变成如下形式:

while 1 do

 

    exitThread=IsThreadExit();

    ySleep(100);

    if exitThread == "exit" then

        break;

    end;

end;

 

VC代码如下,其中的Output是一个输出函数,你可以用MessageBox来代替:

 

// 全局变量

 

// 标志线程是否要结束

BOOL g_bExitDofile = FALSE;

// 线程句柄

HANDLE g_hDofile = NULL;

 

 

// 需要导出的函数

 

// 让 Lua 判断循环是否可以退出

static int IsThreadExit(lua_State* L)

{

 if(g_bExitDofile)

  lua_pushlstring(L, "exit", 4);

 else

  lua_pushlstring(L, "Notexit", 7);

 

 // 一个返回值

 return 1;

}

 

// 停顿函数

int ySleep(lua_State* L)

{

 int d = luaL_checkinteger(L, 1);

 Sleep(d);

 

 return 0;

}

 

// 注册以上函数

int RegFunc()

{

        lua_pushcfunction(g_pLua, IsThreadExit);

        lua_setglobal(g_pLua, "IsThreadExit");

 

        lua_pushcfunction(g_pLua, ySleep);

        lua_setglobal(g_pLua, "ySleep");

 

        return 0;

}

 

 

// 线程函数

DWORD WINAPI DofileThread(LPVOID lpParam)

{

  LPCTSTR strFilePath = (LPCTSTR)lpParam;

 luaL_dofile(g_pLua, strFilePath);

  

 StackDump(g_pLua);

 

 return 0;

}

 

// 线程启动

int StartRun(LPCTSTR strFilePath)

{

 // 注册所有需要导出的函数

 RegFunc();

 if(g_hDofile == NULL)

 {

  // 创建线程来执行LUA脚本

  g_hDofile = CreateThread(NULL, 0, DofileThread, (LPVOID)strFilePath, 0, NULL);

 }

 else

 {

  Output("请先调用StopRun()");

 }

 return 0;

}

 

// 停止线程

int StopRun()

{

 // 如果线程正在跑

 if(g_hDofile)

 {

  // 给lua循环结束的信号

  g_bExitDofile = TRUE;

  // 等待线程退出

  DWORD dwRet = WaitForSingleObject(g_hDofile, INFINITE);

  

  // 如果顺利退出

  if(dwRet == WAIT_OBJECT_0)

  {

   CloseHandle(g_hDofile);

   g_hDofile = NULL;

   g_bExitDofile = FALSE;

   

   Output("Dofile thread exited!");

  }

  // 否则强硬杀掉线程

  else

  {

   DWORD dwExitCode; 

   GetExitCodeThread(g_hDofile, &dwExitCode);

   TerminateThread(g_hDofile, dwExitCode);

   g_hDofile = NULL;

   g_bExitDofile = FALSE;

   

   Output("Dofile thread was killed!");

  }

 }

 return 0;

}

 

上面这个方法是利用了线程的本身特性解决掉脚本阻塞的问题。还可以使用LUA本身的HOOKS机制来防止脚本的阻塞,这个方法下次再说了。。。