8

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

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

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

另外,我是否可以说在类方法中定义的锁仅适用于该类的特定实例?意义:

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

每个呼叫是否能够同时发生?

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

4

3 回答 3

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

于 2009-08-02T17:03:04.330 回答
8

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

在您的问题中,您有一个分配 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

于 2009-08-02T16:41:54.367 回答
2

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

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

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

于 2009-08-02T16:53:36.070 回答