C ++ / GLFW - правильный способ использования объектов Mutex? - PullRequest
0 голосов
/ 15 июня 2009

Я работаю над симуляцией, которая широко использует многопоточность. Дело в том, что до сих пор я никогда не использовал объекты мьютекса для защиты своих данных. И в результате я получаю кучу ошибок сегментации ..

Я пытаюсь заблокировать / разблокировать с помощью мьютекса во время: чтения / записи, но это вызывает у меня еще один segfault:

#0 77D27DD2 ntdll!RtlEnumerateGenericTableLikeADirectory() (C:\Windows\system32\ntdll.dll:??)
#1 00000000 ??() (??:??)

Конечно, я создал тестовый проект, в котором применил блокировку / разблокировку для базовой ситуации, и это сработало, вот базовый пример, который показывает, как работать с объектами Mutex с помощью GLFW:

#include <GL/glfw.h>
#include <iostream>
#include <vector>

using namespace std;

vector<int> table;
GLFWmutex th_mutex;
void GLFWCALL Thread_1(void* arg) {


    glfwLockMutex(th_mutex);
    table.pop_back();
    glfwUnlockMutex(th_mutex);
}

void GLFWCALL Thread_2(void* arg) {

    glfwLockMutex(th_mutex);
    table.erase(table.begin());
    glfwUnlockMutex(th_mutex);

}

int main()
{

    bool    running = true;
    GLFWthread th_1, th_2;

    glfwInit();

    if( !glfwOpenWindow( 512, 512, 0, 0, 0, 0, 0, 0, GLFW_WINDOW ) )
    {
        glfwTerminate();
        return 0;
    }

    glfwSetWindowTitle("GLFW Application");

    for(int i = 0;i < 10; i++) {
        table.push_back(i);
    }


    th_mutex = glfwCreateMutex();
    th_1 = glfwCreateThread(Thread_1, NULL);
    th_2 = glfwCreateThread(Thread_2, NULL);


    while(running)
    {

        // exit if ESC was pressed or window was closed
        running = !glfwGetKey(GLFW_KEY_ESC) && glfwGetWindowParam( GLFW_OPENED);
    }

    glfwTerminate();

    return 0;
}

Проект, над которым я работаю, больше, у меня запущено 5 потоков, и к множеству векторов, карт и очередей обращаются одновременно. Где-то в коде я пытался сделать что-то вроде:

void GLFWCALL CreateVehicleThread(void* arg) {

     int index = (*static_cast<PulseStateByEntrance*>(arg)).index;
     double t_initial = (*static_cast<PulseStateByEntrance*>(arg)).initial_time;
     double t_final = (*static_cast<PulseStateByEntrance*>(arg)).final_time;
     int pulse = (*static_cast<PulseStateByEntrance*>(arg)).pulse;
     int nb_entrance = (*static_cast<PulseStateByEntrance*>(arg)).nb_entrance;
     int min_time_creation = static_cast<int>(ceil(3600 / pulse));


     while((glfwGetTime() - (*static_cast<PulseStateByEntrance*>(arg)).initial_time)
     < ((*static_cast<PulseStateByEntrance*>(arg)).final_time - (*static_cast<PulseStateByEntrance*>(arg)).initial_time)) {


           double t_elapsed = glfwGetTime() - t_initial;


           if(t_elapsed > min_time_creation) {

                 **int nb_vehicle_per_cycle = static_cast<int>((t_elapsed * pulse)/3600);
                 glfwLockMutex(th_mutex);
                 VehicleManager::CreateVehicles(nb_vehicle_per_cycle, nb_entrance);
                 glfwUnlockMutex(th_mutex);**
                 t_initial = glfwGetTime();

           }

    }


}

Причина, по которой я помещаю свой метод VehicleManager: CreateVehicles () между блокировкой / разблокировкой, заключается в том, что в этом методе есть строка:

VehicleManager::vehicles_.push_back(vehicle);

Итак, я хотел защитить вектор: Vehicles_. Но, как результат, я получил этот segfault выше. И даже с:

glfwLockMutex(th_mutex);
VehicleManager::vechicles_.push_back(vehicle);
glfwUnlockMutex(th_mutex);

У меня тот же сегмент.

Надеюсь, я прояснил себя достаточно, чтобы вы поняли природу моей проблемы. Полагаю, не все из вас работали с GLFW, поэтому я дал вам первый базовый пример, чтобы вы могли понять, как мьютексы работают с этой библиотекой.

Спасибо!

Ответы [ 2 ]

1 голос
/ 15 июня 2009

Вы можете обернуть ваши контейнеры, что облегчит ваш код:

template<typename T>
class MultithreadedVector
{
public:
    void pushback( T data )
    {
        glfwLockMutex(m_mutex);
        m_container.push_back( data );
        glfwUnlockMutex(m_mutex);
    }
//then similar for erase etc
private:
    std::vector<T> m_container;
    GLFWmutex m_mutex;
};
1 голос
/ 15 июня 2009

Недостаточно информации. Но, как кажется, похоже, что мьютекс не был создан правильно (выглядит как пустая ссылка). Найдите точку, где вы вызываете glfwCreateMutex (), и убедитесь, что возвращено правильное значение.

Не относится к вашей проблеме, поэтому примечание:

не делайте этого:

glfwLockMutex(th_mutex);
<ACTION>
glfwUnlockMutex(th_mutex);

Это стиль C и, как следствие, небезопасное исключение.
Вы должны установить класс так, чтобы блокировка вызывалась в конструкторе, а разблокировка вызывалась в деструкторе. Тогда имейте объект блокировки этого класса. Таким образом, блокировка и разблокировка обычно выполняются как часть процедуры создания объекта, поэтому при наличии исключений блокировка будет правильно снята.

Принцип RAII, и здесь много статей на эту тему.

Пример:

class MutexLocker
{
    public:
    MutexLocker(GLFWmutex& mutex)
        :m_mutex(mutex)
    {
        glfwLockMutex(m_mutex);
    }
    ~MutexLocker()
    {
        glfwUnlockMutex(m_mutex);
    }
    private:
        GLFWmutex& m_mutex;
};

void myAction()
{
    MutexLocker   lock(th_mutex);

    // Do stuff here:
}
...