multithreading 什么时候需要条件变量,互斥量还不够吗?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/12551341/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
When is a condition variable needed, isn't a mutex enough?
提问by legends2k
I'm sure mutex isn't enough that's the reason the concept of condition variables exist; but it beats me and I'm not able to convince myself with a concrete scenario when a condition variable is essential.
我确信互斥量是不够的,这就是条件变量概念存在的原因;但它打败了我,当条件变量必不可少时,我无法用具体的场景说服自己。
Differences between Conditional variables, Mutexes and Locksquestion's accepted answer says that a condition variable is a
Conditional variables, Mutexes and Locks问题的接受答案之间的差异说条件变量是一个
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
用“信号”机制锁定。当线程需要等待资源变得可用时使用它。线程可以在 CV 上“等待”,然后资源生产者可以“发信号”变量,在这种情况下,等待 CV 的线程会得到通知并可以继续执行
Where I get confused is that, a thread can wait on a mutex too, and when it gets signalled, is simply means the variable is now available, why would I need a condition variable?
我感到困惑的是,一个线程也可以等待互斥锁,当它发出信号时,只是意味着该变量现在可用,为什么我需要一个条件变量?
P.S.: Also, a mutex is required to guard the condition variable anyway, when makes my vision more askew towards not seeing condition variable's purpose.
PS:此外,无论如何都需要互斥锁来保护条件变量,当使我的视力更倾向于看不到条件变量的目的时。
采纳答案by slowjelj
Even though you can use them in the way you describe, mutexes weren't designed for use as a notification/synchronization mechanism. They are meant to provide mutually exclusive access to a shared resource. Using mutexes to signal a condition is awkward and I suppose would look something like this (where Thread1 is signaled by Thread2):
尽管您可以按照您描述的方式使用它们,但互斥锁并不是为用作通知/同步机制而设计的。它们旨在提供对共享资源的互斥访问。使用互斥体来表示条件很尴尬,我想看起来像这样(其中 Thread1 由 Thread2 发出信号):
Thread1:
线程 1:
while(1) {
lock(mutex); // Blocks waiting for notification from Thread2
... // do work after notification is received
unlock(mutex); // Tells Thread2 we are done
}
Thread2:
线程2:
while(1) {
... // do the work that precedes notification
unlock(mutex); // unblocks Thread1
lock(mutex); // lock the mutex so Thread1 will block again
}
There are several problems with this:
这有几个问题:
- Thread2 cannot continue to "do the work that precedes notification" until Thread1 has finished with "work after notification". With this design, Thread2 is not even necessary, that is, why not move "work that precedes" and "work after notification" into the same thread since only one can run at a given time!
- If Thread2 is not able to preempt Thread1, Thread1 will immediately re-lock the mutex when it repeats the while(1) loop and Thread1 will go about doing the "work after notification" even though there was no notification. This means you must somehow guarantee that Thread2 will lock the mutex before Thread1 does. How do you do that? Maybe force a schedule event by sleeping or by some other OS-specific means but even this is not guaranteed to work depending on timing, your OS, and the scheduling algorithm.
- Thread2 不能继续“执行通知之前的工作”,直到 Thread1 完成“通知后的工作”。有了这种设计,Thread2 就没有必要了,也就是说,为什么不将“先行工作”和“通知后工作”移到同一个线程中,因为在给定时间只能运行一个!
- 如果 Thread2 无法抢占 Thread1,则 Thread1 将在重复 while(1) 循环时立即重新锁定互斥锁,并且即使没有通知,Thread1 也会继续执行“通知后工作”。这意味着您必须以某种方式保证 Thread2 会在 Thread1 之前锁定互斥锁。你是怎样做的?也许通过睡眠或其他一些特定于操作系统的方式强制调度事件,但即使这也不能保证工作取决于时间、您的操作系统和调度算法。
These two problems aren't minor, in fact, they are both major design flaws and latent bugs. The origin of both of these problems is the requirement that a mutex is locked and unlocked within the same thread. So how do you avoid the above problems? Use condition variables!
这两个问题都不是小问题,事实上,它们都是主要的设计缺陷和潜在的错误。这两个问题的根源是要求在同一线程内锁定和解锁互斥锁。那么如何避免上述问题呢?使用条件变量!
BTW, if your synchronization needs are really simple, you could use a plain old semaphore which avoids the additional complexity of condition variables.
顺便说一句,如果您的同步需求真的很简单,您可以使用一个普通的旧信号量来避免条件变量的额外复杂性。
回答by Dagang
Mutex is for exclusive access of shared resource, while conditional variable is for waiting for a condition to be true. People may think they can implement conditional variable without the support of kernel. A common solution one might come up with is the "flag + mutex" is like:
互斥用于共享资源的独占访问,而条件变量用于等待条件成立。人们可能认为他们可以在没有内核支持的情况下实现条件变量。人们可能想出的一种常见解决方案是“标志 + 互斥锁”,如下所示:
lock(mutex)
while (!flag) {
sleep(100);
}
unlock(mutex)
do_something_on_flag_set();
but it will never work, because you never release the mutex during the waiting, no one else can set the flag in a thread-safe way. This is why we need conditional variable, when you're waiting on a condition variable, the associated mutex is not hold by your thread until it's signaled.
但它永远不会工作,因为在等待期间您永远不会释放互斥锁,没有其他人可以以线程安全的方式设置标志。这就是为什么我们需要条件变量的原因,当您等待条件变量时,相关联的互斥锁在收到信号之前不会被您的线程保留。
回答by MBI
I was thinking about this too and most important information, which I was missing everywhere was that mutex can own (and change) at the time only one thread. So if you have one producer and more consumers, producer would have to wait on mutex to produce. With cond. variable he can produce at any time.
我也在考虑这一点,最重要的信息,我到处都遗漏的是互斥锁在当时只能拥有(和更改)一个线程。因此,如果您有一个生产者和多个消费者,那么生产者将不得不等待互斥锁来生产。随着条件。他可以随时生产的变量。
回答by Frank M
The conditional var and the mutex pair can be replaced by a binary semaphore and mutex pair. The sequence of operations of a consumer thread when using the conditional var + mutex is:
条件变量和互斥对可以替换为二进制信号量和互斥对。使用条件 var + mutex 时,消费者线程的操作顺序为:
Lock the mutex
Wait on the conditional var
Process
Unlock the mutex
锁定互斥锁
等待条件变量
过程
解锁互斥锁
The producer thread sequence of operations is
生产者线程的操作顺序是
Lock the mutex
Signal the conditional var
Unlock the mutex
锁定互斥锁
向条件变量发出信号
解锁互斥锁
The corresponding consumer thread sequence when using the sema+mutex pair is
使用sema+mutex对时对应的consumer线程序列为
Wait on the binary sema
Lock the mutex
Check for the expected condition
If the condition is true, process.
Unlock the mutex
If the condition check in the step 3 was false, go back to the step 1.
等待二进制 sema
锁定互斥锁
检查预期条件
如果条件为真,则处理。
解锁互斥锁
如果步骤 3 中的条件检查为假,则返回步骤 1。
The sequence for the producer thread is:
生产者线程的顺序是:
Lock the mutex
Post the binary sema
Unlock the mutex
锁定互斥锁
发布二进制 sema
解锁互斥锁
As you can see the unconditional processing in the step 3 when using the conditional var is replaced by the conditional processing in the step 3 and step 4 when using the binary sema.
如您所见,当使用条件变量时,步骤 3 中的无条件处理被步骤 3 和步骤 4 中使用二进制 sema 时的条件处理所取代。
The reason is that when using sema+mutex, in a race condition, another consumer thread may sneak in between the step 1 and 2 and process/nullify the condition. This won't happen when using conditional var. When using the conditional var, the condition is guarantied to be true after the step 2.
原因是当使用 sema+mutex 时,在竞争条件下,另一个消费者线程可能会在步骤 1 和 2 之间潜入并处理/取消条件。使用条件变量时不会发生这种情况。使用条件变量时,条件在第 2 步之后保证为真。
The binary semaphore can be replaced with the regular counting semaphore. This may result in the step 6 to step 1 loop a few more times.
二进制信号量可以替换为常规计数信号量。这可能会导致步骤 6 到步骤 1 循环多次。
回答by Basile Starynkevitch
You need condition variables, to be used with a mutex (each cond.var. belongs to a mutex) to signal changing states (conditions) from one thread to another one. The idea is that a thread can wait till some condition becomes true. Such conditions are program specific (i.e. "queue is empty", "matrix is big", "some resource is almost exhausted", "some computation step has finished" etc). A mutex might have several related condition variables. And you need condition variables because such conditions may not always be expressed as simply as "a mutex is locked" (so you need to broadcast changes in conditions to other threads).
您需要条件变量,与互斥锁(每个 cond.var. 属于一个互斥锁)一起使用,以指示从一个线程到另一个线程的状态(条件)变化。这个想法是一个线程可以等到某个条件变为真。这些条件是特定于程序的(即“队列为空”、“矩阵很大”、“某些资源几乎耗尽”、“某些计算步骤已完成”等)。一个互斥体可能有几个相关的条件变量。并且您需要条件变量,因为这些条件可能并不总是简单地表示为“互斥锁被锁定”(因此您需要将条件的变化广播到其他线程)。
Read some good posix thread tutorials, e.g. this tutorialor thator thatone. Better yet, read a good pthread book. See this question.
阅读一些好的 posix 线程教程,例如本教程或那个或那个教程。更好的是,阅读一本好的 pthread 书。看到这个问题。
Also read Advanced Unix Programmingand Advanced Linux Programming
P.S. Parallelism and threads are difficult concepts to grasp. Take time to read and experiment and read again.
PS 并行和线程是难以掌握的概念。花时间阅读和实验,然后再阅读。
回答by Mr. Ree
I think it is implementation defined.
The mutex is enough or not depends on whether you regard the mutex as a mechanism for critical sections or something more.
我认为它是实现定义的。
互斥量是否足够取决于您是否将互斥量视为临界区的机制或更多。
As mentioned in http://en.cppreference.com/w/cpp/thread/mutex/unlock,
如http://en.cppreference.com/w/cpp/thread/mutex/unlock 中所述,
The mutex must be locked by the current thread of execution, otherwise, the behavior is undefined.
互斥锁必须被当前执行线程锁定,否则行为未定义。
which means a thread could only unlock a mutex which was locked/owned by itself in C++.
But in other programming languages, you might be able to share a mutex between processes.
这意味着一个线程只能解锁一个在 C++ 中被自己锁定/拥有的互斥锁。
但是在其他编程语言中,您可能能够在进程之间共享互斥锁。
So distinguishing the two concepts may be just performance considerations, a complex ownership identification or inter-process sharing are not worthy for simple applications.
所以区分这两个概念可能只是性能方面的考虑,复杂的所有权标识或者进程间共享对于简单的应用来说是不值得的。
For example, you may fix @slowjelj's case with an additional mutex (it might be an incorrect fix):
例如,您可以使用额外的互斥锁来修复 @slowjelj 的情况(这可能是一个不正确的修复):
Thread1:
线程 1:
lock(mutex0);
while(1) {
lock(mutex0); // Blocks waiting for notification from Thread2
... // do work after notification is received
unlock(mutex1); // Tells Thread2 we are done
}
Thread2:
线程2:
while(1) {
lock(mutex1); // lock the mutex so Thread1 will block again
... // do the work that precedes notification
unlock(mutex0); // unblocks Thread1
}
But your program will complain that you have triggered an assertion left by the compiler (e.g. "unlock of unowned mutex" in Visual Studio 2015).
但是您的程序会抱怨您触发了编译器留下的断言(例如,Visual Studio 2015 中的“unlock of unowned mutex”)。