61

例如c++0x 接口

我很难弄清楚什么时候使用这些东西(cv、mutex 和 lock)。任何人都可以请解释或指向资源吗?

提前致谢。

4

3 回答 3

85

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

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

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

于 2009-06-28T18:28:09.187 回答
5

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

回复:互斥锁与锁:从您发布的文档中,看起来 amutex是一个表示操作系统互斥锁lock的对象,而 a 是一个持有互斥锁以促进RAII 模式的对象。

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

就一般参考资料而言:这本书(尚未出版)看起来很有趣。

于 2009-06-28T18:28:37.737 回答
3

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

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

我很难弄清楚什么时候使用这些东西(cv、mutex 和 lock)。任何人都可以请解释或指向资源吗?

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

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

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

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

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

于 2019-09-12T10:28:50.450 回答