[英]LuaPlus: How to call Lua function from multithreaded C++?
I have a kind of callback function in my Lua script which I would like to call from different threads on the C++ side (0-100 times per second). 我的Lua脚本中有一种回调函数,我想从C ++端的不同线程中调用(每秒0-100次)。 So far it basically work, but as soon as I call it multiple times in a very short period of time it crashes the program causing errors like: 到目前为止,它基本上可以正常工作,但是一旦我在很短的时间内多次调用它,它就会使程序崩溃,并导致如下错误:
-As????ion failed: 0, file ...LuaFunction.h, line 146
or this one (completely random) -As????ion failed: 0, file ...LuaFunction.h, line 146
或这一 -As????ion failed: 0, file ...LuaFunction.h, line 146
(完全随机)
I think this happens, when it gets called from the C++ side before it finished another task. 我认为,当它在完成另一项任务之前从C ++端调用时,就会发生这种情况。 The most obvious thing for me to try (mutex lock all threads during the lua-function call) didn't help at all. 对我而言,最显而易见的尝试(在lua函数调用期间互斥锁锁定所有线程)根本没有帮助。 :/ :/
If I only call the Lua-function like once per 2 seconds, then I don't get any errors at all (Well, until the clean up part, if it gets to that point it will crash without a specific error). 如果我仅每2秒调用一次Lua函数,那么我根本不会收到任何错误(嗯,直到清理部分,如果达到这一点,它将崩溃而没有特定的错误)。
Here is my code (I tried to crop and simplify my code as much as possible, and added a lot of commenting): 这是我的代码(我尝试尽可能地裁剪和简化我的代码,并添加了很多注释):
#include "stdafx.hpp"
#include <pthread.h> //for multithreading
#include <windows.h>
#include <iostream>
#include <map>
using namespace std;
unsigned int maxThreads = 100;
map<unsigned int, pthread_t> threads;
map<unsigned int, bool> threadsState;
pthread_mutex_t mutex; //to lock the pthreads (to keep printing from overlapping etc)
LuaPlus::LuaState* pState = LuaPlus::LuaState::Create( true ); //initialize LuaPlus
LuaPlus::LuaObject globals = pState->GetGlobals();
struct argumentStruct { //to pass multiple arguments to the function called when starting a pthread
unsigned int threadId;
int a;
int b;
};
map<unsigned int, struct argumentStruct> argumentMap; //we store the arguments of active threads in here
void *ThreadFunction(void *arguments) { //will be called for every pthread we're going to create
struct argumentStruct*args = (struct argumentStruct*)arguments; //get the arrgument struct
int threadId = args->threadId; //get variables for each struct field
int a = args->a;
int b = args->b;
Sleep(3000); //since this is a very simplified version of my actual project
int c = a+b;
pthread_mutex_lock(&mutex); //lock pthreads for the next lines
LuaPlus::LuaFunction<int> CPP_OnMyEvent = pState->GetGlobal("LUA_OnMyEvent"); //get the Lua callback function to call on the C++ side
CPP_OnMyEvent(a,b,c); //call to our lua-callback function
pthread_mutex_unlock(&mutex); //unlock pthreads
threadsState[threadId] = false; //mark the thread as finished/ready to get overwritten by a new one
return NULL;
}
bool AddThread(int a, int b) {
for (;;) {
if (threads.size() < maxThreads) { //if our array of threads isn't full yet, create a new thread
int id = threads.size();
argumentMap[id].threadId = threads.size();
argumentMap[id].a = a;
argumentMap[id].b = b;
threadsState[id] = true; //mark the thread as existing/running
pthread_create(&threads[id], NULL, &ThreadFunction, (void *)&argumentMap[id]);
return true;
} else {
unsigned int id;
for (auto thread=threads.begin(); thread!=threads.end(); ++thread) {
id = thread->first;
if(!threadsState[id]) { //if thread with id "id" has finished, create a new thread on it's pthread_t
argumentMap[id].threadId = id;
argumentMap[id].a = a;
argumentMap[id].b = b;
threadsState[id] = true; //mark the thread as existing/running
pthread_join(threads[id], NULL);
pthread_create(&threads[id], NULL, &ThreadFunction, (void *)&argumentMap[id]);
return true;
}
}
}
}
return false;
}
int main() {
pthread_mutex_init(&mutex, NULL); //initialize the mutex
//LuaPlus::LuaState* pState = LuaPlus::LuaState::Create( true ); //we already initialized this globally
//LuaPlus::LuaObject globals = pState->GetGlobals();
//pState->DoString("function LUA_OnMyEvent(arg1,arg2) print(arg1..arg2) end"); //it's already in main.lua
globals.RegisterDirect("AddThread", AddThread);
char pPath[ MAX_PATH ];
GetCurrentDirectory(MAX_PATH,pPath);
strcat_s(pPath,MAX_PATH,"\\main.lua");
if( pState->DoFile(pPath) ) { //run our main.lua script which contains the callback function that will run a print
if( pState->GetTop() == 1 )
std::cout << "An error occured: " << pState->CheckString(1) << std::endl;
}
for (auto thread=threads.begin(); thread!=threads.end(); ++thread) { //wait for threads to finish
unsigned int id = thread->first;
if(threadsState[id])
pthread_join(threads[id], NULL);
}
//clean up
LuaPlus::LuaState::Destroy( pState );
pState = nullptr;
pthread_mutex_destroy(&mutex);
getchar(); //keep console from closing
return 0;
}
main.lua main.lua
function LUA_OnMyEvent(a,b,c)
print(a.."+"..b.."="..c)
end
for i=1, 999, 1 do
AddThread(i,i*2)
end
I don't know Lua enough to give you a solution at Lua side, but this view of the problem may help you reaching that out. 我对Lua的了解还不足以在Lua方面为您提供解决方案,但是这种问题的观点可能会帮助您实现这一目标。
When you call AddThread() from Lua, something like this will happen: 当您从Lua调用AddThread()时,将发生以下情况:
1. LuaState allocations
2. AddThread() execution
3. LuaState unwinding
While on ThreadFunction()... 在ThreadFunction()上时...
A. Mutex lock
B. LuaState allocations
C. LUA_OnMyEvent() execution
D. LuaState unwinding
E. Mutex Unlock
There is no mutex control at AddThread, so a race condition can happen between 1/3 and B/D. AddThread没有互斥锁控件,因此竞争条件可能发生在1/3和B / D之间。 However, adding the mutex to AddThread would not solve the problem, because it would still run between 1 and 3. 但是,将互斥锁添加到AddThread并不能解决问题,因为它仍然可以在1到3之间运行。
If AddThread() is called only at the program initialization, then you could block all threads till initialization is done. 如果AddThread()仅在程序初始化时调用,则可以阻塞所有线程,直到完成初始化。 If it is called frequently during program execution, then I would make those calls from a separate LuaState. 如果在程序执行期间经常调用它,那么我将从单独的LuaState进行这些调用。
[EDIT] 2nd idea: Use a producer/consumer approach. [编辑]第二个想法:使用生产者/消费者方法。 Then C++ threads won't need to run Lua code. 然后,C ++线程将不需要运行Lua代码。
C++ suggestion: C ++建议:
//-- start Task.h --
struct Task{
static list<Task*> runningTasks;
static list<Task*> doneTasks;
static pthread_mutex_t mutex;
list<Task*>::iterator iterator;
virtual ~Task(){}
bool start(){
pthread_mutex_lock(&mutex);
bool hasSpace = runningTasks.size() < 100;
if(hasSpace){
runningTasks.push_front(this);
iterator = runningTasks.begin();
pthread_t unusedID;
pthread_create(&unusedID, NULL, Task::threadBody, this);
}
pthread_mutex_unlock(&mutex);
return hasSpace;
}
virtual void run() = 0;
virtual void processResults() = 0;
protected:
void finish(){
pthread_mutex_lock(&mutex);
runningTasks.erase(iterator);
doneTasks.push_front(this);
pthread_mutex_unlock(&mutex);
}
static void* threadBody(void* instance){
Task* task = static_cast<Task*>(instance);
task->run();
task->finish();
return NULL;
}
};
//-- end Task.h --
//-- start Task.cpp --
//Instantiate Task's static attributes
pthread_mutex_t Task::mutex;
list<Task*> Task::runningTasks;
list<Task*> Task::doneTasks;
//-- end Task.cpp --
struct SumTask: public Task{
int a, b, c;
void run(){
Sleep(3000);
c = a+b;
}
void processResults(){
LuaPlus::LuaFunction<int> CPP_OnMyEvent = pState->GetGlobal("LUA_OnMyEvent");
CPP_OnMyEvent(a,b,c);
}
}
//functions called by Lua
bool runSumTask(int a, int b){
SumTask task* = new SumTask();
task->a = a; task->b = b;
bool ok = task->start();
if(!ok)
delete task;
return ok;
}
int gatherResults(){
pthread_mutex_lock(&Task::mutex);
int totalResults = Task::doneTasks.size();
while(Task::doneTasks.size() > 0){
Task* t = Task::doneTasks.front();
Task::doneTasks.pop_front();
t->processResults();
delete t;
}
pthread_mutex_unlock(&Task::mutex);
return totalResults;
}
int main() {
//Must initialize/destroy Task::mutex
pthread_mutex_init(&Task::mutex, NULL);
//...
pthread_mutex_destroy(&Task::mutex);
}
Lua code: Lua代码:
function LUA_OnMyEvent(a,b,c)
print(a.."+"..b.."="..c)
end
local totalRunning = 0;
for i=1, 999, 1 do
if (runSumTask(i,i*2))
totalRunning = totalRunning + 1;
totalRunning -= gatherResults();
end
while(totalRunning > 0) do
totalRunning -= gatherResults();
mySleepFunction(...);
end
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.