У меня есть функция обратного вызова в моем сценарии 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 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 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
Я не знаю 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.
[EDIT] Вторая идея: используйте подход производителя/потребителя. Затем для потоков 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 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