互斥锁上的锁也适用于被调用的函数吗?

时间:2009-08-02 16:39:34

标签: multithreading qt mutex

如果在函数中定义了互斥锁,它的锁是否适用于从该函数调用的函数?即

void f () {  
    Mutex mutex;  
    g();  
}

锁仍然适用于g()中的任何数据修改吗?

另外,我是否正确地说类方法中定义的锁只适用于该类的特定实例?含义:

Class Foo;  
Foo foo1, foo2;
(In thread 1) foo1.bar();  
(In thread 2) foo2.bar();  

每个电话会同时发生吗?

如果有人可以解释/指出解释互斥体背后机制的链接,那将是一个很好的奖励。谢谢!我目前正在使用Qt Thread库,如果该信息有帮助的话。

3 个答案:

答案 0 :(得分:17)

在您的示例中,您实际上并未锁定互斥锁,因此它不会阻止不同的线程同时访问该函数。您还在函数内部声明了互斥锁,以便每个函数调用使用不同的本地互斥对象。即使这个互斥锁被锁定,每个函数调用也会锁定一个不同的互斥对象,而不会阻止同时访问。

更好的策略是这样的设置:

class A {
  QMutex mutex;

  void f() {  
    QMutexLocker ml(mutex); // Acquire a lock on mutex
    g();

    // The lock on the mutex will be released when ml is destroyed.
    // This happens at the end of this function.
  }

  // ...
};

在这种情况下,mutex只要存在ml就会被锁定,因此在线程花费在g()内的时间也是如此。如果另一个线程在此期间调用f(),它将阻止创建其ml对象,直到第一个线程离开该函数并且新线程可以锁定mutex。 / p>

答案 1 :(得分:8)

互斥锁是你抓住的东西,并且会阻止任何其他线程试图抓住它,直到你从抓取线程中释放它。

在您的问题中,您有一个功能f分配Mutex实例。这还不足以锁定它。你必须专门调用mutex.lock()(在Qt中,但一般情况下,除非你使用pthread,在这种情况下使用pthread_mutex_lock并享受低级别,平台相关的东西.Qt很好地抽象了。)

这是Qt的一个例子

  void MyClass::doStuff( int c )
    {
        mutex.lock();
        a = c;
        b = c * 2;
        mutex.unlock();
    } 

一旦你获得锁定,对g()的调用将从获得锁定的线程中完成,因此在该调用假设中你将不会调用g()来自代码另一部分的其他线程。锁定并不意味着它将停止所有其他线程。它将阻止线程尝试获得相同的锁,直到锁被释放。

如果这是您的线程到达g()的唯一方法,那么您将在该访问权限上同步。

对于问题的第二部分,如果互斥锁是实例属性,那么它们将是两个不同的互斥锁。您将必须声明并实例化类互斥实例,并将其引用为您的锁定。在这种情况下,任何在类中锁定类互斥锁的方法调用的尝试都将被有效地同步,这意味着没有两个线程将一起执行该方法。

例如(我没有Qt,所以我无法编译这段代码,2年前我停止使用它进行编码,所以它无法正常工作)

class Foo {
public:
   void method(void) {
      mutex.lock();
      cout << "method called";
      // long computation
      mutex.unlock();
   }

private:
  QMutex mutex;
};

好的,在这种情况下,假设你有两个线程,1和2,以及类Foo,a和b的两个实例。假设线程1调用a.method()并且线程2调用b.method()。在这种情况下,两个互斥锁是不同的实例,因此每个线程将独立执行调用,并且并行运行。

假设您有两个线程,1和2,以及在两个线程之间共享的类Foo的一个实例。如果线程1调用a.method()然后线程2调用a.method(),则线程2将停止并等待直到释放互斥锁。

最后,

class Foo {
public:
   void method(void) {
      mutex.lock();
      cout << "method called";
      // long computation
      mutex.unlock();
   }

private:
  static QMutex mutex;
};

QMutex Foo::mutex;

在这种情况下,互斥锁是一个类静态变量。每个对象实例只有一个互斥锁实例。假设您遇到与上述第一种情况相同的情况:两个线程和两个实例。在这种情况下,当第二个线程试图调用b.method()时,它必须等待第一个线程完成a.method(),因为锁现在是唯一的并且在类的所有实例之间共享。

有关更多信息,Qt有一个很好的多线程教程

https://doc.qt.io/qt-5/threads.html

答案 2 :(得分:2)

您的互斥锁在堆栈上本地实例化。因此,从一个线程调用f()将锁定其互斥锁的自己的实例。从另一个线程对f()的任何其他调用将锁定它自己的。因此,从g()访问数据可能会发生竞争条件!甚至很难在相同的类实例上调用它:

MyClass foo;
(In thread 1) foo->f();
(In thread 2) foo->f();

如何更好地处理锁定取决于您想要做什么。根据你所说的,我猜一个更好的策略是直接修改g()实现:它必须锁定一个声明为全局的互斥锁,或者在g()中保持静态,以便在对g()的任何调用之间共享。只要我了解您想要全局锁定数据?