Question

I'm trying to use a recursive QMutex, i read the QMutex Class Reference but i not understand how to do it, can someone give me an example? I need some way to lock QMutex that can be unlocked after or before the lock method is called. If recursive mutex is not the way is there any other way?

Était-ce utile?

La solution

To create a recursive QMutex you simply pass QMutex::Recursive at construction time, for instance:

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

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

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

Recursive means that you can lock several times the mutex from the same thread, you don't have to unlock it. If I understood well your question that's what you want.

Be careful, if you lock recursively you must call unlock the same amount of times. A better way to lock/unlock a mutex is using a 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;
}

Autres conseils

A recursive mutex can be locked multiple times from a single thread without needing to be unlocked, as long as the same number of unlock calls are made from the same thread. This mechanism comes in handy when a shared resource is used by more then one function, and one of those functions call another function in which the resource is used.

Consider the following class:

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;
}

An initial implementation may look something like the following:

Foo::Foo() {}

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

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

The above code will DEADLOCK on calls to thud. mLock will be acquired in the first line of thud() and once again by the first line of bar() which will block waiting for thud() to release the lock.

A simple solution would be to make the lock recursive in the ctor.

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

This an OK fix, and will be suitable for many situations, however one should be aware that there may be a performance penalty to using this solution since each recursive mutex call may require a system call to identify the current thread id.

In addition to the thread id check, all calls to thud() still execute QMutex::lock() twice!

Designs which require a recursive may be able to be refactored to eliminate the need for the recursive mutex. In general, the need for a recursive mutex is a "code smell" and indicates a need to adhere to the principle of separation of concerns.

For the class Foo, one could imagine creating a private function call which performs the shared computation and keeping the resource locking at the public interface level.

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
}

Recursive mode just means that if a thread owns a mutex, and the same thread tries to lock the mutex again, that will succeed. The requirement is that calls to lock/unlock are balanced.

In non recursive mode, this will result in a deadlock.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top