【问题标题】:do mutex's in C++ have to be tied to some object or variable?C++ 中的互斥锁是否必须绑定到某个对象或变量?
【发布时间】:2013-02-02 19:20:58
【问题描述】:

我对线程有些陌生,我正在尝试了解它在 C++11 中的工作原理。我班的教授给了我们这个示例代码来演示互斥锁的使用:

#include <list> 
#include <mutex> 
#include <algorithm>

std::list<int> some_list; // A data structure accessed by multiple threads
std::mutex some_mutex; // This lock will prevent concurrent access to the shared data structure

void
add_to_list(int new_value) {
    std::lock_guard<std::mutex> guard(some_mutex); // Since I am going to access the shared data struct, acquire the lock
    some_list.push_back(new_value); // Now it is safe to use some_list. RAII automatically releases lock at end of function }
}

bool
list_contains(int value_to_find) {
    std::lock_guard<std::mutex> guard(some_mutex); // Must get lock every time I access some_list return
    std::find (some_list.begin(),some_list.end(),value_to_find) != some_list.end();
}

我认为代码有点不言自明,但我有一些具体问题。

  1. 是否不需要专门将互斥锁与列表相关联?
  2. 如果不是,这是否意味着任何时候使用互斥锁,所有线程都会停止,直到互斥锁被销毁?或者它只是线程的一个子集;也许某个线程池中的线程或以其他方式相互关联?
  3. 无论是哪种情况,只停止试图访问数据结构的线程不是更好吗?因为否则,我们不会担心数据竞争等问题。
  4. 最后,互斥锁和锁有什么区别?互斥锁只是一个 RAII 锁吗?或者 RAII 是通过守卫发生的?

【问题讨论】:

  • 这些都是很好的问题。质疑系统的详细工作原理是一种非常有用的态度,尤其是当您不熟悉特定技术时。
  • 您的教授正在教您现代 C++?哇!
  • 是的,教授实际上是标准委员会的成员:D
  • 互斥锁是“门上的锁”。 lock_guard 是一个“锁定和解锁门上的锁”的对象。

标签: c++ multithreading mutex


【解决方案1】:
  1. 互斥体与列表相关联,但这种关联完全是手动的——编译器和运行时库不知道两者是关联的。该关联完全存在于您的文档和您的脑海中,您有责任确保访问列表的任何线程首先锁定/获取互斥锁。

  2. 每当使用互斥锁时,锁定/获取互斥锁的线程将停止(该术语实际上是block),直到没有其他线程拥有该互斥锁。不使用互斥锁的线程将不受影响。

  3. 您负责确保只有访问列表的线程锁定/获取互斥锁,并且您还负责确保访问列表的所有线程锁定/获取互斥锁。同样,只有那些线程可以阻塞等待互斥体。

  4. 同一个对象有许多不同的名称:“mutex”、“lock”或“critical section”。守卫使用 RAII 锁定/获取互斥锁。

【讨论】:

  • 也许使用不同的词来代替 halt。它让我想起了这一点:en.wikipedia.org/wiki/HLT。从这个意义上说,你所说的并不完全正确。 #4 - 互斥体、锁和临界区不是不同名称的同一个对象。
  • @thang: block 常用。
  • @thang:如果您能向我解释其中的区别,而不是仅仅说“它们不是一回事”,那就太好了。据我所知,Windows中的CRITICAL_SECTION和Mutex都等价于pthread_mutex_t,这些都等价于Java或Python等系统中所谓的“锁”。它们都具有相同的接口和相同的语义,差异与此功能无关。
  • @DietrichEpp,你可以试试谷歌搜索,这是来自 google stackoverflow.com/questions/2808617/… 的第一个链接。 差异与此功能无关同一个对象有许多不同的名称不同。
  • @DietrichEpp:在 Windows 中,临界区在同一线程上时是可重入的。默认情况下,pthread 互斥锁不是,我相信标准 C++ 互斥锁也不是。在 C++ 中,锁和互斥锁不是一回事,锁作用于互斥锁(或多个互斥锁)。我同意,尽管从概念上和功能上讲,这些都是实现同一目标的不同方式。
【解决方案2】:

是否不需要专门将互斥锁与列表相关联?

不,或者至少不是明确的,而且通常以声明的方式也不可能。每次需要访问受该互斥锁保护的对象时,您的代码都必须(尝试)获取适当的互斥锁,并在完成读取或更改其状态后释放它。关于哪个互斥锁保护哪个对象的知识只存在于程序员的脑海中(当然也存在于程序的文档中)。

如果不是,这是否意味着任何时候使用互斥锁,所有线程都会停止,直到互斥锁被销毁?或者它只是线程的一个子集;可能是某个线程池中的线程或以其他方式相互关联的线程?

这意味着所有想要对某个对象执行原子序列操作的线程首先必须获取互斥锁,该互斥锁保护该对象以与竞争相同资源的其他线程同步访问。互斥锁保证只有一个线程可以拥有它,并且在该线程释放(而不是“销毁”)互斥锁之前,所有其他线程都将等待获取它。

无论是哪种情况,只停止试图访问数据结构的线程不是更好吗?因为否则,我们不担心数据竞争等。

是的,确实如此。通过强制需要独占访问某个对象的线程(并且仅是那些线程)获取保护它的互斥锁,您不会强制其他线程停止。

最后,互斥锁和锁有什么区别?互斥锁只是一个 RAII 锁吗?或者 RAII 是通过守卫发生的?

互斥锁是客户端可以(尝试)获取的同步对象,以便在受该互斥锁保护的对象上执行某些操作,而不必担心其他线程会干扰其操作(只要其他线程也遵守协议)当然,在访问对象之前尝试获取互斥锁)。

锁通常被理解为一个 RAII 包装对象,它封装了您对互斥锁的锁定,并且其析构函数在调用时会自动解锁互斥锁。因此,当锁超出范围时(因为从函数返回,或者因为抛出异常等),获取的互斥锁会自动释放。

【讨论】:

    【解决方案3】:

    是否不需要专门将互斥锁与列表相关联?

    不,没有。在这种情况下,互斥锁只是保护代码块,而不是列表本身。因为some_list.push_back()std::find是在被同一个互斥锁保护的代码块中执行的,所以单独的线程不会在被保护的块中一起传播,直到一个线程退出该块。

    如果不是,这是否意味着任何时候使用互斥锁,所有线程都会停止,直到互斥锁被销毁?或者它只是线程的一个子集;可能是某个线程池中的线程或以其他方式相互关联的线程?

    否 - 所有试图进入受互斥锁保护的块的线程都被挂起,直到互斥锁解锁(这可能通过销毁持有互斥锁的 lock_guard 对象来实现)。

    无论是哪种情况,只停止试图访问数据结构的线程不是更好吗?因为否则,我们不担心数据竞争等。

    正如我所说,只有试图访问受保护块的线程可能会被挂起,因此没有必要阻塞所有线程。

    最后,互斥锁和锁有什么区别?互斥锁只是一个 RAII 锁吗?或者 RAII 是通过守卫发生的?

    不,互斥锁只是同步原语,在不同系统上的实现方式不同,但 C++ 为其提供了统一的接口。 mutex 本身只知道三个操作:locktry_lockunlock,所以它周围有不同的包装器。其中之一,提供 RAII 是std::lock_guard

    【讨论】:

      【解决方案4】:

      是否不需要专门将互斥锁与列表相关联?

      没有。您手动执行此操作。

      如果不是,这是否意味着任何时候使用互斥锁,所有线程 停止直到互斥锁被破坏?或者它只是线程的一个子集; 可能是某个线程池中的线程或与每个线程相关联的线程 其他的?

      mutex = 互斥。如果两个线程试图锁定互斥锁,其中一个线程将阻塞,直到 那个 互斥锁被释放。如果您尝试在另一个线程有锁的情况下锁定一个互斥锁,它将阻塞直到它被释放。

      无论是哪种情况,只停止线程不是更好吗? 正在尝试访问数据结构?因为否则,我们 不担心数据竞争等。

      是的。当并发访问出现问题时,您应该只锁定保护数据结构的互斥锁。例如。修改数据结构时。这就是为什么您的 add_to_list 函数仅在 some_list.push_back() 期间锁定互斥锁 (some_mutex)。

      最后,互斥锁和锁有什么区别?是互斥体 只是一个 RAII 锁?

      C++ lock_guard 是一个围绕互斥锁的 RAII 包装器。当对象被创建时,互斥锁被锁定,当它被销毁(超出范围)时,互斥锁被解锁。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-10-21
        • 2013-12-17
        • 1970-01-01
        • 2015-10-31
        • 2011-09-06
        • 1970-01-01
        • 1970-01-01
        • 2013-04-16
        相关资源
        最近更新 更多