【问题标题】:How to use recursive QMutex如何使用递归 QMutex
【发布时间】:2012-11-30 01:47:18
【问题描述】:

我正在尝试使用递归 QMutex,我阅读了 QMutex 类参考,但我不明白该怎么做,有人可以举个例子吗? 我需要一些方法来锁定 QMutex,可以在调用 lock 方法之后或之前解锁。 如果递归互斥锁不是办法,还有其他办法吗?

【问题讨论】:

  • 您能再详细解释一下您的需求吗?你是什​​么意思你需要它在锁定方法之前或之后解锁?你是说方法结束后窗台自动解锁的吗?

标签: c++ qt mutex recursive-mutex


【解决方案1】:

要创建递归 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;
}

【讨论】:

    【解决方案2】:

    只要从同一个线程进行相同数量的解锁调用,就可以从单个线程多次锁定递归互斥锁,而无需解锁。当一个共享资源被多个函数使用,并且其中一个函数调用另一个使用该资源的函数时,这种机制会派上用场。

    考虑以下类:

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

    上面的代码在调用 thud 时会死锁。 mLock 将在 thud() 的第一行获取,然后再次由 bar() 的第一行获取,这将阻塞等待 thud() 释放锁。

    一个简单的解决方案是使锁在 ctor 中递归。

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

    这是一个不错的修复,适用于许多情况,但是应该注意,使用此解决方案可能会降低性能,因为每个递归互斥锁调用可能需要系统调用来识别当前线程 ID。

    除了线程id检查之外,所有对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
    }
    

    【讨论】:

      【解决方案3】:

      递归模式只是意味着如果一个线程拥有一个互斥锁,并且同一个线程再次尝试锁定互斥锁,那将成功。要求是对lock/unlock 的调用是平衡的。

      在非递归模式下,这将导致死锁。

      【讨论】:

        猜你喜欢
        • 2012-02-16
        • 1970-01-01
        • 2015-06-08
        • 2014-04-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-02-03
        • 2017-07-23
        相关资源
        最近更新 更多