Как использовать рекурсивный QMutex

Я пытаюсь использовать рекурсивный QMutex, я читаю Справочник по классам QMutex, но я не понимаю, как это сделать, может кто-нибудь дать мне пример? Мне нужен какой-то способ блокировки QMutex, который можно разблокировать после или до вызова метода блокировки. Если рекурсивный мьютекс не является способом, есть ли другой путь?

3 ответа

Решение

Чтобы создать рекурсивный QMutex, вы просто передаете QMutex::Recursive во время строительства, например:

QMutex mutex(QMutex::Recursive);
int number = 6;

void method1()
{
    mutex.lock();
    number *= 5;
    mutex.unlock();
}

void method2()
{
    mutex.lock();
    number *= 3;
    mutex.unlock();
}

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

Будьте осторожны, если вы рекурсивно блокируете, вы должны вызывать анлок столько же раз. Лучший способ заблокировать / разблокировать мьютекс - использовать QMutexLocker

#include <QMutexLocker>

QMutex mutex(QMutex::Recursive);
int number = 6;

void method1()
{
    QMutexLocker locker(&mutex); // Here mutex is locked
    number *= 5;
    // Here locker goes out of scope.
    // When locker is destroyed automatically unlocks mutex
}

void method2()
{
    QMutexLocker locker(&mutex);
    number *= 3;
}

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

Рассмотрим следующий класс:

class Foo {
  public:
    Foo();
    void bar();    // Does something to the resource
    void thud();   // Calls bar() then does something else to the resource
  private:
    Resource mRes;
    QMutex mLock;
}

Начальная реализация может выглядеть примерно так:

Foo::Foo() {}

void Foo::bar() {
  QMutexLocker locker(&mLock);
  mRes.doSomething();
}

void Foo::thud() {
  QMutexLocker locker(&mLock);
  bar();
  mRes.doSomethingElse();
}

Приведенный выше код будет DEADLOCK при вызовах Thud. mLock будет получен в первой строке thud() и еще раз первой строкой bar(), которая будет блокировать ожидание thud(), чтобы снять блокировку.

Простым решением было бы сделать рекурсивную блокировку в ctor.

Foo::Foo() : mLock(QMutex::Recursive) {}

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

В дополнение к проверке идентификатора потока, все вызовы thud() все еще выполняют QMutex::lock() дважды!

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

Для класса Foo можно представить создание вызова частной функции, которая выполняет совместное вычисление и поддерживает блокировку ресурса на уровне открытого интерфейса.

class Foo {
  public:
    Foo();
    void bar();    // Does something to the resource
    void thud();   // Does something then does something else to the resource
  private:
    void doSomething();
  private:
    Resource mRes;
    QMutex mLock;
}

Foo::Foo() {}

// public
void Foo::bar() {
  QMutexLocker locker(&mLock);
  doSomething();
}

void Foo::thud() {
  QMutexLocker locker(&mLock);
  doSomething();
  mRes.doSomethingElse();
}

// private
void Foo::doSomething() {
  mRes.doSomething();        // Notice - no mutex in private function
}

Рекурсивный режим просто означает, что если поток владеет мьютексом, и тот же поток пытается снова заблокировать мьютекс, это будет успешно выполнено. Требование заключается в том, что звонки lock/unlock сбалансированы.

В нерекурсивном режиме это приведет к тупику.

Другие вопросы по тегам