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

0

У меня есть функция обратного вызова в моем сценарии 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
  • 0
    Состояния lua не безопасны для потоков. Вы не можете использовать одно и то же состояние из нескольких потоков одновременно. Вы читали lua-users.org/wiki/ThreadsTutorial ?
  • 0
    @EtanReisner Я не уверен, действительно ли это относится к LuaPlus. Я также пытался создать состояние LuaPlus для каждого потока, но это не сработало вообще. Я думаю, что новое состояние LuaPlus в основном создает новую среду сценариев, которая не имеет доступа к остальным. И то, что с мьютексом уже есть в моем коде и мало чем помогает.
Показать ещё 4 комментария
Теги:
callback
function
lua
luaplus

1 ответ

1

Я не знаю 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
  • 0
    Весь смысл моей программы в том, что она постоянно выполняет определенный код асинхронно, поэтому мне приходится динамически добавлять потоки. Я не вижу способа реализовать это по-другому.
  • 1
    @Forivin Производитель-потребитель на самом деле то, что вам нужно. Lua в своем собственном потоке в бесконечном цикле блокировки вызовов функции give_me_next_task (), а C ++ - сторона этой функции, ожидающая в общей очереди.
Показать ещё 16 комментариев

Ещё вопросы

Сообщество Overcoder
Наверх
Меню