LuaPlus: Как вызвать функцию Lua из многопоточного C ++?

У меня есть своего рода функция обратного вызова в моем скрипте Lua, которую я хотел бы вызвать из разных потоков на стороне C ++ (0-100 раз в секунду). Пока это в основном работает, но как только я вызываю его несколько раз за очень короткий промежуток времени, происходит сбой программы, вызывающий такие ошибки, как:
-As????ion failed: 0, file ...LuaFunction.h, line 146 или же этот (совершенно случайно)

Я думаю, что это происходит, когда он вызывается со стороны C ++, прежде чем он завершил другую задачу. Самая очевидная вещь для меня (мьютексная блокировка всех потоков во время вызова функции lua) совсем не помогла. : /
Если я вызываю только Lua-функцию, как раз в 2 секунды, то я вообще не получаю никаких ошибок (ну, до тех пор, пока часть очистки не достигнет этой точки, она не будет работать без определенной ошибки).

Вот мой код (я постарался максимально упростить и упростить мой код и добавил много комментариев):

#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

function LUA_OnMyEvent(a,b,c)
print(a.."+"..b.."="..c)
end

for i=1, 999, 1 do
AddThread(i,i*2)
end

0

Решение

Я недостаточно знаю Lua, чтобы дать вам решение на стороне Lua, но этот взгляд на проблему может помочь вам решить эту проблему.

Когда вы вызываете AddThread () из Lua, произойдет нечто подобное:

1. LuaState allocations
2. AddThread() execution
3. LuaState unwinding

В то время как на ThreadFunction () …

A. Mutex lock
B. LuaState allocations
C. LUA_OnMyEvent() execution
D. LuaState unwinding
E. Mutex Unlock

В AddThread нет управления мьютексом, поэтому состояние гонки может быть между 1/3 и B / D.
Тем не менее, добавление мьютекса в AddThread не решит проблему, потому что он все еще будет работать между 1 и 3.

Если AddThread () вызывается только при инициализации программы, то вы можете заблокировать все потоки до завершения инициализации. Если он часто вызывается во время выполнения программы, я бы сделал эти вызовы из отдельного LuaState.

[РЕДАКТИРОВАТЬ] 2-я идея: Используйте подход производителя / потребителя. Тогда потокам C ++ не нужно будет запускать код Lua.

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:

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
1

Другие решения


По вопросам рекламы [email protected]