Я пытаюсь использовать рекурсивный QMutex, я читаю Справочник по классам QMutex, но я не понимаю, как это сделать, может кто-нибудь дать мне пример?
Мне нужен какой-то способ блокировки QMutex, который можно разблокировать после или до вызова метода блокировки.
Если рекурсивный мьютекс не является способом, есть ли другой путь?
Чтобы создать рекурсивный 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
означает, что вы можете заблокировать мьютекс несколько раз из одного потока, вам не нужно его разблокировать. Если я правильно понял твой вопрос, это то, что ты хочешь.
Будьте осторожны, если вы делаете рекурсивную блокировку, вы должны вызывать unlock одинаковое количество раз. Лучший способ заблокировать / разблокировать мьютекс — использовать 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
сбалансированы.
В нерекурсивном режиме это приведет к тупику.