c++多執行緒呼叫lua方法,會時不時崩潰。
修改了lua源代碼lua_lock和lua_unlock宏,加鎖情況下也是會崩潰(參考http://www.cnblogs.com/zhangdongsheng/p/3679024.html)
下面是我的代碼。
=========================================== test.lua 測驗代碼如下:
local t = Thread:getInstance(); // 這里是下面c++映射到lua的方法
for i = 1, 10, 1 do
// 啟動c++執行緒執行下面lua方法
t:start(function()
// 這里是新執行緒里處理
for j = 1, 100000, 1 do
G:log(i .. " #### j:" .. tostring(j));
end
end);
end
=========================================== lua_thread.cpp 代碼如下:(下面代碼是注冊Thread.h中的函式為lua)
lua中可以通過
local t = Thread:getInstance(); 獲得執行緒物件,也可以 local t = Thread:new();
t:start(function()
// 這里是新執行緒里處理
end);
--------------------------------------------
/*
** Lua binding: ThreadToLua
*/
#include "lua_thread.hpp"
#ifndef __cplusplus
#include "stdlib.h"
#endif
#include "string.h"
#include "tolua_fix.h"
#include "Thread.h"
#include "CCLuaValue.h"
USING_NS_CC;
/* Exported function */
//TOLUA_API int register_lua_thread(lua_State* tolua_S);
/* function to register type */
static void tolua_reg_types(lua_State* tolua_S)
{
tolua_usertype(tolua_S, "Thread");
}
/* method: getInstance of class Thread */
#ifndef TOLUA_DISABLE_tolua_thread_getInstance
static int tolua_thread_getInstance(lua_State* tolua_S)
{
#ifndef TOLUA_RELEASE
tolua_Error tolua_err;
if (
!tolua_isusertable(tolua_S, 1, "Thread", 0, &tolua_err) ||
!tolua_isnoobj(tolua_S, 2, &tolua_err)
)
goto tolua_lerror;
else
#endif
{
{
Thread* tolua_ret = (Thread*)Thread::getInstance();
tolua_pushusertype(tolua_S, (void*)tolua_ret, "Thread");
}
}
return 1;
#ifndef TOLUA_RELEASE
tolua_lerror :
tolua_error(tolua_S, "#ferror in function 'getInstance'.", &tolua_err);
return 0;
#endif
}
#endif //#ifndef TOLUA_DISABLE}
/* method: newInstance of class Thread */
#ifndef TOLUA_DISABLE_tolua_thread_newInstance
static int tolua_thread_newInstance(lua_State* tolua_S)
{
#ifndef TOLUA_RELEASE
tolua_Error tolua_err;
if (
!tolua_isusertable(tolua_S, 1, "Thread", 0, &tolua_err) ||
!tolua_isnoobj(tolua_S, 2, &tolua_err)
)
goto tolua_lerror;
else
#endif
{
{
Thread* tolua_ret = (Thread*)Thread::newInstance();
tolua_pushusertype(tolua_S, (void*)tolua_ret, "Thread");
}
}
return 1;
#ifndef TOLUA_RELEASE
tolua_lerror :
tolua_error(tolua_S, "#ferror in function 'newInstance'.", &tolua_err);
return 0;
#endif
}
#endif //#ifndef TOLUA_DISABLE
/* method: start of class Thread */
#ifndef TOLUA_DISABLE_tolua_thread_start
static int tolua_thread_start(lua_State* tolua_S)
{
#ifndef TOLUA_RELEASE
tolua_Error tolua_err;
if (
!tolua_isusertype(tolua_S, 1, "Thread", 0, &tolua_err) ||
//!tolua_isnumber(tolua_S,2,0,&tolua_err) ||
!toluafix_isfunction(tolua_S, 2, "LUA_FUNCTION", 0, &tolua_err) ||
!tolua_isnoobj(tolua_S, 3, &tolua_err)
)
goto tolua_lerror;
else
#endif
{
Thread* self = (Thread*)tolua_tousertype(tolua_S, 1, 0);
//int luaCallback = ((int) tolua_tonumber(tolua_S,2,0));
LUA_FUNCTION luaCallback = (toluafix_ref_function(tolua_S, 2, 0));
#ifndef TOLUA_RELEASE
if (!self) tolua_error(tolua_S, "invalid 'self' in function 'start'", NULL);
#endif
{
self->start(luaCallback);
}
}
return 0;
#ifndef TOLUA_RELEASE
tolua_lerror :
tolua_error(tolua_S, "#ferror in function 'start'.", &tolua_err);
return 0;
#endif
}
#endif //#ifndef TOLUA_DISABLE
/* Open function */
TOLUA_API int register_lua_thread(lua_State* tolua_S)
{
tolua_open(tolua_S);
tolua_reg_types(tolua_S);
tolua_module(tolua_S, NULL, 0);
tolua_beginmodule(tolua_S, NULL);
tolua_cclass(tolua_S, "Thread", "Thread", "", NULL);
tolua_beginmodule(tolua_S, "Thread");
tolua_function(tolua_S, "getInstance", tolua_thread_getInstance);
tolua_function(tolua_S, "new", tolua_thread_newInstance);
tolua_function(tolua_S, "start", tolua_thread_start);
tolua_endmodule(tolua_S);
tolua_endmodule(tolua_S);
return 1;
}
#if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM >= 501
TOLUA_API int luaopen_thread(lua_State* tolua_S) {
return register_lua_thread(tolua_S);
};
#endif
=========================================== Thread.h 代碼如下:
#ifndef __LUA_Thread_H__
#define __LUA_Thread_H__
#include "cocos2d.h"
#include "CCLuaStack.h"
#include "ThreadSafeQueue.h"
class Thread
{
public:
Thread();
~Thread();
static inline Thread* getInstance()
{
static Thread *pInstance = nullptr;
if (nullptr == pInstance)
{
pInstance = new Thread();
}
return pInstance;
}
static inline Thread* newInstance()
{
Thread *pInstance = new Thread();
return pInstance;
}
void start(int luaFunctionId);
private:
void handleLuaFunctionQueue(cocos2d::LuaStack* newthread);
void execThreadLuaFunction(cocos2d::LuaStack* newthread, int luaFunctionId);
ThreadSafeQueue<int> luaFunctionQueue;
cocos2d::LuaStack* newthread = NULL;
};
#endif
=========================================== Thread.cpp 代碼如下:(主要看這部分代碼實作c++多執行緒呼叫lua)
#include "Thread.h"
#include "CCLuaEngine.h"
#if (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32)
#include <windows.h>
#else
#include <unistd.h>
#endif
Thread::Thread()
{
luaFunctionQueue.clear();
}
Thread::~Thread()
{
luaFunctionQueue.clear();
CC_SAFE_RELEASE(newthread);
}
void Thread::start(int luaFunctionId)
{
if (luaFunctionQueue.empty())
{
luaFunctionQueue.push_back(luaFunctionId);
// 如果之前創建過,這里就洗掉,下面重新創建
CC_SAFE_RELEASE(newthread);
// 在新執行緒啟動之前復制創建堆疊,并且一個執行緒只配一個LuaState
lua_State* L1 = cocos2d::LuaEngine::getInstance()->getLuaStack()->getLuaState();
newthread = cocos2d::LuaStack::newthread(L1); // 這里newthread作為全域變數防止被回收,導致出問題
// 創建新的系統執行緒并從主執行緒分離出來作為子執行緒(子執行緒執行完會自動銷毀)
cocos2d::log("########## Thread start ##########");
auto t = std::thread([&]() {
handleLuaFunctionQueue(newthread);
});
t.detach();
}
else
{
luaFunctionQueue.push_back(luaFunctionId);
}
}
void Thread::handleLuaFunctionQueue(cocos2d::LuaStack* newthread)
{
if (!luaFunctionQueue.empty())
{
int luaFunctionId = luaFunctionQueue.front();
execThreadLuaFunction(newthread, luaFunctionId);
luaFunctionQueue.pop_front();
// 遞回執行佇列下一個luaFunctionId
handleLuaFunctionQueue(newthread);
}
}
void Thread::execThreadLuaFunction(cocos2d::LuaStack* newthread, int luaFunctionId)
{
if (luaFunctionId) {
cocos2d::log("########## start luaFunctionId = %d", luaFunctionId);
newthread->executeFunctionByHandler(luaFunctionId, 0);
cocos2d::log("########## end luaFunctionId = %d", luaFunctionId);
}
}
=========================================== ThreadSafeQueue.h 代碼如下:
#ifndef _THREADSAFEQUEUE_H_
#define _THREADSAFEQUEUE_H_
template<typename T>
class ThreadSafeQueue
{
public:
void push_back( T && item ) {
std::lock_guard<std::mutex> g( _mutex );
_q.push_back( std::forward<T>(item) );
}
void push_back( T const & item ) {
std::lock_guard<std::mutex> g( _mutex );
_q.push_back( item );
}
T &front(){
std::lock_guard<std::mutex> g( _mutex );
return _q.front();
}
void pop_front() {
std::lock_guard<std::mutex> g( _mutex );
if ( !_q.empty() )
{
_q.pop_front();
}
}
bool empty() {
std::lock_guard<std::mutex> g( _mutex );
return _q.empty();
}
void clear() {
std::lock_guard<std::mutex> g( _mutex );
_q.clear();
}
void clear( std::function< void( T & ) > f ) {
std::lock_guard<std::mutex> g( _mutex );
for( auto &o : _q ) f( o );
_q.clear();
}
private:
std::mutex _mutex;
std::deque<T> _q;
};
#endif
uj5u.com熱心網友回復:
下面是cocos2dx-lua專案的 CCLuaStack.cpp 部分代碼,可不看。(主要是上面Thread.cpp用到了newthread方法和executeFunctionByHandler這兩個方法)
代碼太多發不了,只發用到的。
LuaStack *LuaStack::newthread(lua_State *L)
{
lua_State* L2 = lua_newthread(L);
LuaStack *stack = new (std::nothrow) LuaStack();
stack->initWithLuaState(L2);
//stack->autorelease();
return stack;
}
bool LuaStack::initWithLuaState(lua_State *L)
{
_state = L;
return true;
}
int LuaStack::executeFunctionByHandler(int nHandler, int numArgs)
{
int ret = 0;
if (pushFunctionByHandler(nHandler)) /* L: ... arg1 arg2 ... func */
{
if (numArgs > 0)
{
lua_insert(_state, -(numArgs + 1)); /* L: ... func arg1 arg2 ... */
}
ret = executeFunction(numArgs);
}
lua_settop(_state, 0);
return ret;
}
bool LuaStack::pushFunctionByHandler(int nHandler)
{
toluafix_get_function_by_refid(_state, nHandler); /* L: ... func */
if (!lua_isfunction(_state, -1))
{
CCLOG("[LUA ERROR] function refid '%d' does not reference a Lua function", nHandler);
lua_pop(_state, 1);
return false;
}
return true;
}
int LuaStack::executeFunction(int numArgs)
{
int functionIndex = -(numArgs + 1);
if (!lua_isfunction(_state, functionIndex))
{
CCLOG("value at stack [%d] is not function", functionIndex);
lua_pop(_state, numArgs + 1); // remove function and arguments
return 0;
}
int traceback = 0;
lua_getglobal(_state, "__G__TRACKBACK__"); /* L: ... func arg1 arg2 ... G */
if (!lua_isfunction(_state, -1))
{
lua_pop(_state, 1); /* L: ... func arg1 arg2 ... */
}
else
{
lua_insert(_state, functionIndex - 1); /* L: ... G func arg1 arg2 ... */
traceback = functionIndex - 1;
}
int error = 0;
++_callFromLua;
error = lua_pcall(_state, numArgs, 1, traceback); /* L: ... [G] ret */
--_callFromLua;
if (error)
{
if (traceback == 0)
{
CCLOG("[LUA ERROR] %s", lua_tostring(_state, - 1)); /* L: ... error */
lua_pop(_state, 1); // remove error message from stack
}
else /* L: ... G error */
{
lua_pop(_state, 2); // remove __G__TRACKBACK__ and error message from stack
}
return 0;
}
// get return value
int ret = 0;
if (lua_isnumber(_state, -1))
{
ret = (int)lua_tointeger(_state, -1);
}
else if (lua_isboolean(_state, -1))
{
ret = (int)lua_toboolean(_state, -1);
}
// remove return value from stack
lua_pop(_state, 1); /* L: ... [G] */
if (traceback)
{
lua_pop(_state, 1); // remove __G__TRACKBACK__ from stack /* L: ... */
}
return ret;
}
uj5u.com熱心網友回復:
話說lua不是只有一個執行緒嗎,這種多執行緒去掉用lua的寫法似乎本身就不合理?轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/67518.html
標籤:Cocos2d-x
下一篇:用lua寫魔塔游戲 誰有原始碼啊
