Как мне убедиться, что есть только 1 мьютекс? - PullRequest
2 голосов
/ 27 августа 2009

Я запускаю некоторый потокобезопасный код здесь. Я использую мьютекс для защиты раздела кода, который должен запускаться только одним потоком за раз. У меня проблема с использованием этого кода, иногда я получаю 2 объекта Mutex. Кстати, это статическая функция. Как сделать так, чтобы был создан только один объект мьютекса ??

/*static*/ MyClass::GetResource()
{

if (m_mutex == 0)
{
// make a new mutex object
m_mutex = new MyMutex();
}

m_mutex->Lock();

Ответы [ 5 ]

12 голосов
/ 27 августа 2009

Просто создайте m_mutex за пределами GetResource(),, прежде чем его можно будет вызвать - это удалит критическую секцию вокруг фактического создания мьютекса.

MyClass::Init()
{
  m_mutex = new Mutex;
}    

MyClass::GetResource()
{
  m_mutex->Lock();
  ...
  m_mutex->Unlock();
}
8 голосов
/ 27 августа 2009

Проблема в том, что поток может быть прерван после проверки, равен ли m_mutex 0, но не раньше, чем он создаст мьютекс, позволяя другому потоку проходить через тот же код.

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

Вы не упоминаете свою целевую платформу, но в Windows:

MyClass::GetResource()
{
    if (m_mutex == 0)
    {
        // make a new mutex object
        MyMutex* mutex = new MyMutex();

        // Only set if mutex is still NULL.
        if (InterlockedCompareExchangePointer(&m_mutex, mutex, 0) != 0)
        {
           // someone else beat us to it.
           delete mutex;
        }
    }
    m_mutex->Lock();

В противном случае замените любой функцией сравнения / обмена, которую обеспечивает ваша платформа.

Другим вариантом является использование поддержки однократной инициализации , которая доступна в Windows Vista и более поздних версиях, или просто предварительное создание мьютекса, если вы можете.

3 голосов
/ 27 августа 2009

Ленивая инициализация мьютекса не совсем подходит для статических методов; вам нужна гарантия, что никто не поспешит с инициализацией. Далее используется компилятор для генерации одного статического мьютекса для класса.

/* Header (.hxx) */
class MyClass
{
    ...

  private:
    static mutable MyMutex m_mutex;  // Declares, "this mutex exists, somewhere."
};


/* Compilation Unit (.cxx) */
MyMutex MyClass::m_mutex;            // The aforementioned, "somewhere."

MyClass::GetResource()
{
    m_mutex.Lock();
    ...
    m_mutex.Unlock();
}

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

2 голосов
/ 27 августа 2009

Зачем вообще использовать указатель? Почему бы не заменить указатель реальным экземпляром, который не требует динамического управления памятью? Это позволяет избежать состояния гонки и не приводит к снижению производительности при каждом вызове функции.

0 голосов
/ 20 февраля 2014

Поскольку он предназначен только для защиты одного конкретного раздела кода, просто объявите его статическим внутри функции.

static MyClass::GetResource()
{
    static MyMutex mutex;

    mutex.Lock();
    // ...
    mutex.Unlock();

Переменная является локальной переменной со статической продолжительностью хранения. В Стандарте прямо указано:

Реализации разрешено выполнять раннюю инициализацию других переменных области блока со статическим или длительность хранения потока при тех же условиях, что реализация может статически инициализировать переменная со статической или продолжительностью хранения потока в области имен (3.6.2). В противном случае такая переменная инициализируется при первом прохождении контроля через его объявление; такая переменная считается инициализированной после завершение его инициализации. Если инициализация завершается с помощью исключения, инициализация не завершен, поэтому он будет повторен при следующем входе элемента управления в объявление. Если контроль входит объявление одновременно во время инициализации переменной, параллельное выполнение должно ожидать завершение инициализации.

Последнее предложение представляет особый интерес для вас.

...