【问题标题】:Differences between Conditional variables, Mutexes and Locks条件变量、互斥锁和锁的区别
【发布时间】:2010-11-06 12:35:38
【问题描述】:

例如c++0x interfaces

我很难确定何时使用这些东西(cv、mutex 和 lock)中的哪一个。 任何人都可以解释或指向资源吗?

提前致谢。

【问题讨论】:

    标签: c++ multithreading c++11 mutex condition-variable


    【解决方案1】:

    在您所指的页面上,“互斥锁”是实际的低级同步原语。您可以获取一个互斥体然后释放它,并且任何时候只有一个线程可以获取它(因此它是一个同步原语)。递归互斥锁可以被同一个线程多次获取,然后需要被同一线程释放多次才能被其他线程获取。

    这里的“锁”只是一个 C++ 包装类,它在其构造函数中接收一个互斥体,并在析构函数中释放它。它对于为 C++ 作用域建立同步很有用。

    条件变量是一种更高级/高级的同步原语形式,它结合了锁和“信号”机制。当线程需要等待资源可用时使用它。线程可以在 CV 上“等待”,然后资源生产者可以“向”变量发出“信号”,在这种情况下,等待 CV 的线程会收到通知并可以继续执行。互斥锁与 CV 相结合,以避免在另一个线程想要发出信号的同时一个线程开始等待 CV 的竞争条件;那么信号是传递还是丢失是不可控的。

    【讨论】:

    • Isn't waiting (thread1) + signaling (thread2) with convars, 和locking (thread1) + unlocking(thread2) 完全一样?
    • @hydroes:没有。如果 thread1 正在等待锁,则必须有其他线程持有它,以便 thread1 保持阻塞。一旦其他线程释放它,thread1 就可以解除阻塞以获取互斥锁。使用条件变量,任何线程都可以发出信号来解除对等待者的阻塞,而不仅仅是一些一直持有锁的特殊线程。另外,你可以广播一个条件变量,释放所有等待的线程。
    • 缺少的文档。谢谢!
    • @Antti Huima "互斥锁与 CV 相结合,以避免一个线程开始等待 CV 的同时另一个线程想要发出信号的竞争条件;那么它是否是不可控的信号被传递或丢失。”这可能是其他地方缺少的关键解释?
    • 将互斥锁与 CV 结合以避免竞争条件部分不正确。当条件变量上没有服务员时,通知无论如何都会丢失。 Spurios 唤醒也会发生。互斥量和条件变量用于等待共享状态的变化。并且为了消除竞争条件,在更新或读取共享状态时必须锁定互斥锁。与答案中的建议相反,在发出条件变量信号时不需要锁定互斥锁(只需阅读std::condition_variable::notify_onepthread_cond_signal 的文档)。
    【解决方案2】:

    我对 C++0x 不太熟悉,所以请对这个答案持保留态度。

    re:互斥锁与锁:从您发布的文档中,看起来mutex 是一个表示操作系统互斥锁的对象,而lock 是一个包含互斥锁以方便RAII pattern 的对象。

    条件变量是一种将阻塞/信号机制(信号+等待)与互斥机制相关联的便捷机制,同时在操作系统中保持它们解耦,以便系统程序员可以选择 condvar 和互斥体之间的关联。 (对于处理多组并发访问的对象很有用)Rob Krten 在他的book on QNX 的一个在线章节中有一些good explanations on condvars

    就一般参考而言:This book(尚未发布)看起来很有趣。

    【讨论】:

      【解决方案3】:

      这个问题已经回答了。我只是添加这可能有助于决定何时使用这些同步原语。

      简单来说,互斥锁就是用来保证多线程临界区共享资源的相互访问。运气是一个通用术语,但二进制互斥锁可以用作锁。在现代 C++ 中,我们使用 lock_guard 和类似的对象来利用 RAII 来简化和确保互斥锁的使用。条件变量是另一种原语,它经常与互斥体结合使用,以形成monitor

      我很难确定何时使用这些东西中的哪一个 (简历、互斥锁和锁)。谁能解释或指出一个 资源?

      使用互斥锁来保证对某些东西的互斥访问。它是广泛的并发问题的默认解决方案。如果你在 C++ 中有一个作用域,你想用互斥锁来保护它,请使用 lock_guard。互斥锁由 lock_guard 处理。您只需在范围内创建一个 lock_guard 并使用互斥锁对其进行初始化,然后 C++ 为您完成其余工作。当范围从堆栈中移除时,互斥锁被释放,包括抛出异常或从函数返回。这是RAII 背后的想法,而 lock_guard 是另一个资源处理程序。

      有一些并发问题仅使用互斥锁不容易解决,或者简单的解决方案可能会导致复杂性或效率低下。例如,produced-consumer problem 就是其中之一。如果我们想实现一个消费者线程从一个与生产者共享的缓冲区中读取项目,我们应该使用互斥锁保护缓冲区,但是,如果不使用条件变量,我们应该锁定互斥锁,检查缓冲区并读取一个项目,如果它不为空,解锁并等待一段时间,再次锁定并继续。如果缓冲区经常是空的(忙于等待)并且会有很多锁定和解锁和休眠,那是浪费时间。

      我们需要的生产者-消费者问题的解决方案必须更简单、更高效。监视器(互斥体 + 条件变量)在这里为我们提供帮助。我们仍然需要一个互斥锁来保证互斥访问,但是条件变量可以让我们休眠并等待某个条件。这里的条件是生产者向缓冲区添加一个项目。生产者线程通知消费者线程缓冲区中有物品,消费者醒来并获取物品。简单地说,生产者锁定互斥体,将一些东西放入缓冲区,通知消费者。消费者锁定互斥体,在等待条件时休眠,当缓冲区中有东西时唤醒并从缓冲区中获取项目。这是一种更简单、更高效的解决方案。

      下次遇到并发问题时可以这样想:如果需要互斥访问,请使用互斥锁。如果您想更安全、更简单,请使用 lock_guard。如果问题有线索等待必须在另一个线程中发生的条件,您可能需要一个条件变量。

      作为一般经验法则,首先,分析您的问题并尝试找到与您的问题相似的著名并发问题(例如,请参阅this page 中的经典同步问题部分)。阅读为众所周知的解决方案提出的解决方案,以达到最佳解决方案。您可能需要进行一些自定义。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-03-12
        • 2011-04-13
        • 2015-06-19
        • 2011-08-28
        • 2014-06-24
        • 1970-01-01
        • 2013-12-17
        相关资源
        最近更新 更多