条件变量、互斥锁和锁之间的区别
For example the C++11 interfaces
I am having a hard time figuring out when to use which of these things (cv, mutex and lock).
Can anyone please explain or point to a resource?
Thanks in advance.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
在您引用的页面上,“互斥体”是实际的低级同步原语。 您可以获取一个互斥体,然后释放它,并且在任何单一时间只有一个线程可以获取它(因此它是一种同步原语)。 递归互斥体是一种可以被同一个线程多次获取的互斥体,然后需要由同一个线程多次释放它,然后其他线程才能获取它。
这里的“锁”只是一个 C++ 包装类,它在构造函数中获取互斥体并在析构函数中释放它。 它对于建立 C++ 作用域的同步很有用。
条件变量是同步原语的更高级/高级形式,它将锁与“信号”机制相结合。 当线程需要等待资源变得可用时使用它。 线程可以“等待” CV,然后资源生产者可以“向该变量发出信号”,在这种情况下,等待 CV 的线程会得到通知并可以继续执行。 互斥量与 CV 相结合,以避免出现竞争情况,即一个线程开始等待 CV,同时另一个线程想要向它发出信号; 那么信号是送达还是丢失是不可控的。
On the page you refer to, "mutex" is the actual low-level synchronizing primitive. You can take a mutex and then release it, and only one thread can take it at any single time (hence it is a synchronizing primitive). A recursive mutex is one which can be taken by the same thread multiple times, and then it needs to be released as many times by the same thread before others can take it.
A "lock" here is just a C++ wrapper class that takes a mutex in its constructor and releases it at the destructor. It is useful for establishing synchronizing for C++ scopes.
A condition variable is a more advanced / high-level form of synchronizing primitive which combines a lock with a "signaling" mechanism. It is used when threads need to wait for a resource to become available. A thread can "wait" on a CV and then the resource producer can "signal" the variable, in which case the threads who wait for the CV get notified and can continue execution. A mutex is combined with CV to avoid the race condition where a thread starts to wait on a CV at the same time another thread wants to signal it; then it is not controllable whether the signal is delivered or gets lost.
这个问题已经得到解答。 我只是添加了这一点,这可能有助于决定何时使用这些同步原语。
简单来说,互斥量是用来保证多个线程对临界区共享资源的相互访问。 运气是一个通用术语,但二进制互斥体可以用作锁。 在现代 C++ 中,我们使用 lock_guard 和类似对象来利用 RAII 来简化互斥体的使用并使其安全。 条件变量是另一个原语,通常与互斥体结合使用,使某些东西成为监视器。
使用互斥锁来保证对某些内容的互斥访问。 它是各种并发问题的默认解决方案。 如果您在 C++ 中有一个作用域并且希望使用互斥体来保护它,请使用 lock_guard。 互斥体由lock_guard 处理。 您只需在作用域中创建一个 lock_guard 并使用互斥锁对其进行初始化,然后 C++ 会为您完成其余的工作。 当由于任何原因(包括引发异常或从函数返回)从堆栈中删除作用域时,互斥体将被释放。 这是 RAII 背后的想法,lock_guard 是另一个资源处理程序。
有些并发问题仅使用互斥体无法轻松解决,或者简单的解决方案可能会导致复杂性或效率低下。 例如,生产者-消费者问题就是其中之一。 如果我们想要实现一个消费者线程从与生产者共享的缓冲区中读取项目,我们应该使用互斥体保护缓冲区,但是,如果不使用条件变量,我们应该锁定互斥体,检查缓冲区并读取项目(如果它不为空) ,解锁并等待一段时间,再次锁定并继续。 如果缓冲区经常为空(忙于等待),那么就会浪费时间,而且还会有大量的锁定、解锁和睡眠。
我们需要的生产者-消费者问题的解决方案必须更简单、更高效。 监视器(互斥锁+条件变量)可以帮助我们。 我们仍然需要互斥锁来保证互斥访问,但条件变量让我们可以休眠并等待特定条件。 这里的条件是生产者向缓冲区添加一个项目。 生产者线程通知消费者线程缓冲区中有项目,消费者醒来并获取该项目。 简单地说,生产者锁定互斥体,将某些内容放入缓冲区,通知消费者。 消费者锁定互斥体,在等待条件时休眠,当缓冲区中有东西时唤醒并从缓冲区中获取该项目。 这是一个更简单、更有效的解决方案。
下次遇到并发问题时,请这样想:如果需要对某些内容进行互斥访问,请使用互斥体。 如果你想更安全、更简单,请使用lock_guard。 如果问题有等待另一个线程中必须发生的条件的线索,您可能需要一个条件变量。
作为一般经验法则,首先,分析您的问题并尝试找到与您的问题类似的著名并发问题(例如,请参阅 此页面)。 了解针对众所周知的解决方案提出的解决方案,以达到最佳解决方案的峰值。 您可能需要一些定制。
This question has been answered. I just add this that may help to decide WHEN to use these synchronization primitives.
Simply, the mutex is used to guarantee mutual access to a shared resource in the critical section of multiple threads. The luck is a general term but a binary mutex can be used as a lock. In modern C++ we use lock_guard and similar objects to utilize RAII to simplify and make safe the mutex usage. The conditional variable is another primitive that often combined with a mutex to make something know as a monitor.
Use a mutex to guarantee mutual exclusive access to something. It's the default solution for a broad range of concurrency problems. Use lock_guard if you have a scope in C++ that you want to guard it with a mutex. The mutex is handled by the lock_guard. You just create a lock_guard in the scope and initialize it with a mutex and then C++ does the rest for you. The mutex is released when the scope is removed from the stack, for any reason including throwing an exception or returning from a function. It's the idea behind RAII and the lock_guard is another resource handler.
There are some concurrency issues that are not easily solvable by only using a mutex or a simple solution can lead to complexity or inefficiency. For example, the produced-consumer problem is one of them. If we want to implement a consumer thread reading items from a buffer shared with a producer, we should protect the buffer with a mutex but, without using a conditional variable we should lock the mutex, check the buffer and read an item if it's not empty, unlock it and wait for some time period, lock it again and go on. It's a waste of time if the buffer is often empty (busy waiting) and also there will be lots of locking and unlocking and sleeps.
The solution we need for the producer-consumer problem must be simpler and more efficient. A monitor (a mutex + a conditional variable) helps us here. We still need a mutex to guarantee mutual exclusive access but a conditional variable lets us sleep and wait for a certain condition. The condition here is the producer adding an item to the buffer. The producer thread notifies the consumer thread that there is and item in the buffer and the consumer wakes up and gets the item. Simply, the producer locks the mutex, puts something in the buffer, notifies the consumer. The consumer locks the mutex, sleeps while waiting for a condition, wake s up when there is something in the buffer and gets the item from the buffer. It's a simpler and more efficient solution.
The next time you face a concurrency problem think this way: If you need mutual exclusive access to something, use a mutex. Use lock_guard if you want to be safer and simpler. If the problem has a clue of waiting for a condition that must happen in another thread, you MIGHT need a conditional variable.
As a general rule of thumb, first, analyze your problem and try to find a famous concurrency problem similar to yours (for example, see classic problems of synchronization section in this page). Read about the solutions proposed for the well-known solution to peak the best one. You may need some customization.
我对 C++0x 不太熟悉,所以对这个答案持保留态度。
回复:互斥体与锁:从您发布的文档来看,互斥体似乎是代表操作系统互斥体的对象,而锁是保存互斥体的对象促进RAII 模式。
条件变量是一种方便的机制,可将阻塞/信号机制(信号+等待)与互斥机制关联起来,同时使它们在操作系统中保持解耦,以便系统程序员可以选择 condvar 和互斥体之间的关联。 (对于处理多组并发访问的对象很有用)Rob Krten 有一些 关于条件变量的很好的解释在他的关于 QNX 的书的在线章节之一中一个>。
至于一般参考:这本书(尚未出版)看起来很有趣。
I'm not too familiar w/ C++0x so take this answer w/ a grain of salt.
re: Mutex vs. locks: From the documentation you posted, it looks like a
mutex
is an object representing an OS mutex, whereas alock
is an object that holds a mutex to facilitate the RAII pattern.Condition variables are a handy mechanism to associate a blocking/signaling mechanism (signal+wait) with a mutual exclusion mechanism, yet keep them decoupled in the OS so that you as system programmer can choose the association between condvar and mutex. (useful for dealing with multiple sets of concurrently-accessed objects) Rob Krten has some good explanations on condvars in one of the online chapters of his book on QNX.
As far as general references: This book (not out yet) looks interesting.