条件变量与信号量
何时使用信号量,何时使用条件变量?
When to use a semaphore and when to use a conditional variable?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
何时使用信号量,何时使用条件变量?
When to use a semaphore and when to use a conditional variable?
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
接受
或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
发布评论
评论(8)
锁用于互斥。当您想确保一段代码是原子的时,请在它周围加一把锁。理论上您可以使用二进制信号量来执行此操作,但这是一种特殊情况。
信号量和条件变量建立在锁提供的互斥之上,用于提供对共享资源的同步访问。它们可用于类似的目的。
条件变量通常用于避免在等待资源可用时忙等待(在检查条件时重复循环)。例如,如果您有一个线程(或多个线程)在队列为空之前无法继续前进,那么繁忙等待方法就是执行以下操作:
这样做的问题是您浪费了处理器时间让该线程反复检查条件。为什么不使用一个同步变量来通知线程资源可用呢?
据推测,您将在其他地方有一个线程将事物从队列中拉出。当队列为空时,它可以调用
syncVar.signal()
来唤醒在syncVar.wait()
上休眠的随机线程(或者通常还有一个signalAll()
或broadcast()
方法唤醒所有正在等待的线程)。当我有一个或多个线程等待某一特定条件(例如队列为空)时,我通常会使用这样的同步变量。
信号量可以类似地使用,但我认为当您拥有一个共享资源时,可以更好地使用它们,该共享资源可以根据某些可用事物的整数个数来可用和不可用。信号量适用于生产者/消费者的情况,其中生产者分配资源而消费者消耗资源。
想象一下您是否有一台汽水自动售货机。只有一台汽水机,并且是共享资源。您有一个线程是负责保持机器库存的供应商(生产商),还有 N 个线程是想要从机器中获取苏打水的买家(消费者)。机器中苏打水的数量是将驱动我们的信号量的整数值。
每个到达汽水机的买家(消费者)线程都会调用信号量
down()
方法来获取汽水。这将从机器中获取一瓶苏打水,并将可用苏打水的数量减 1。如果有可用苏打水,代码将继续运行到down()
语句之后,不会出现任何问题。如果没有可用的苏打水,线程将在此休眠,等待何时再次提供苏打水(当机器中有更多苏打水时)。供应商(生产者)线程本质上是在等待汽水机变空。当最后一批苏打水从机器中取出时,供应商会收到通知(并且一个或多个消费者可能正在等待取出苏打水)。供应商将使用信号量
up()
方法重新进货汽水机,每次可用的汽水数量都会增加,从而等待的消费者线程将收到通知有更多的汽水可用。同步变量的
wait()
和signal()
方法往往隐藏在down()
和up() 中
信号量的操作。当然,这两个选择之间存在重叠。在许多情况下,信号量或条件变量(或条件变量集)都可以满足您的目的。信号量和条件变量都与一个锁对象相关联,它们用于维护互斥,但随后它们在锁之上提供额外的功能以同步线程执行。主要取决于您来确定哪一个最适合您的情况。
这不一定是最技术性的描述,但这就是我脑海中的意义。
Locks are used for mutual exclusion. When you want to ensure that a piece of code is atomic, put a lock around it. You could theoretically use a binary semaphore to do this, but that's a special case.
Semaphores and condition variables build on top of the mutual exclusion provide by locks and are used for providing synchronized access to shared resources. They can be used for similar purposes.
A condition variable is generally used to avoid busy waiting (looping repeatedly while checking a condition) while waiting for a resource to become available. For instance, if you have a thread (or multiple threads) that can't continue onward until a queue is empty, the busy waiting approach would be to just doing something like:
The problem with this is that you're wasting processor time by having this thread repeatedly check the condition. Why not instead have a synchronization variable that can be signaled to tell the thread that the resource is available?
Presumably, you'll have a thread somewhere else that is pulling things out of the queue. When the queue is empty, it can call
syncVar.signal()
to wake up a random thread that is sitting asleep onsyncVar.wait()
(or there's usually also asignalAll()
orbroadcast()
method to wake up all the threads that are waiting).I generally use synchronization variables like this when I have one or more threads waiting on a single particular condition (e.g. for the queue to be empty).
Semaphores can be used similarly, but I think they're better used when you have a shared resource that can be available and unavailable based on some integer number of available things. Semaphores are good for producer/consumer situations where producers are allocating resources and consumers are consuming them.
Think about if you had a soda vending machine. There's only one soda machine and it's a shared resource. You have one thread that's a vendor (producer) who is responsible for keeping the machine stocked and N threads that are buyers (consumers) who want to get sodas out of the machine. The number of sodas in the machine is the integer value that will drive our semaphore.
Every buyer (consumer) thread that comes to the soda machine calls the semaphore
down()
method to take a soda. This will grab a soda from the machine and decrement the count of available sodas by 1. If there are sodas available, the code will just keep running past thedown()
statement without a problem. If no sodas are available, the thread will sleep here waiting to be notified of when soda is made available again (when there are more sodas in the machine).The vendor (producer) thread would essentially be waiting for the soda machine to be empty. The vendor gets notified when the last soda is taken from the machine (and one or more consumers are potentially waiting to get sodas out). The vendor would restock the soda machine with the semaphore
up()
method, the available number of sodas would be incremented each time and thereby the waiting consumer threads would get notified that more soda is available.The
wait()
andsignal()
methods of a synchronization variable tend to be hidden within thedown()
andup()
operations of the semaphore.Certainly there's overlap between the two choices. There are many scenarios where a semaphore or a condition variable (or set of condition variables) could both serve your purposes. Both semaphores and condition variables are associated with a lock object that they use to maintain mutual exclusion, but then they provide extra functionality on top of the lock for synchronizing thread execution. It's mostly up to you to figure out which one makes the most sense for your situation.
That's not necessarily the most technical description, but that's how it makes sense in my head.
让我们揭开幕后的真相。
条件变量本质上是一个等待队列,支持阻塞等待和唤醒操作,即可以将一个线程放入等待队列并将其状态设置为BLOCK,并从中取出一个线程并将其状态设置为 READY。
请注意,要使用条件变量,还需要两个其他元素:
然后协议变为
Semaphore本质上是一个计数器+一个互斥体+一个等待队列。并且可以直接使用,无需外部依赖。您可以将其用作互斥体或条件变量。
因此,信号量可以被视为比条件变量更复杂的结构,而后者更加轻量和灵活。
Let's reveal what's under the hood.
Conditional variable is essentially a wait-queue, that supports blocking-wait and wakeup operations, i.e. you can put a thread into the wait-queue and set its state to BLOCK, and get a thread out from it and set its state to READY.
Note that to use a conditional variable, two other elements are needed:
The protocol then becomes,
Semaphore is essentially a counter + a mutex + a wait queue. And it can be used as it is without external dependencies. You can use it either as a mutex or as a conditional variable.
Therefore, semaphore can be treated as a more sophisticated structure than conditional variable, while the latter is more lightweight and flexible.
信号量可用于实现对变量的独占访问,但它们旨在用于同步。另一方面,互斥体具有与互斥严格相关的语义:只有锁定资源的进程才允许解锁它。
不幸的是,您无法使用互斥体实现同步,这就是我们使用条件变量的原因。另请注意,使用条件变量,您可以使用广播解锁在同一时刻解锁所有等待线程。这不能用信号量来完成。
Semaphores can be used to implement exclusive access to variables, however they are meant to be used for synchronization. Mutexes, on the other hand, have a semantics which is strictly related to mutual exclusion: only the process which locked the resource is allowed to unlock it.
Unfortunately you cannot implement synchronization with mutexes, that's why we have condition variables. Also notice that with condition variables you can unlock all the waiting threads in the same instant by using the broadcast unlocking. This cannot be done with semaphores.
信号量和条件变量非常相似,并且主要用于相同的目的。然而,存在一些细微的差异,可能会使其中一种更可取。例如,要实现屏障同步,您将无法使用信号量。但条件变量是理想的选择。
屏障同步是指您希望所有线程等待,直到每个人都到达线程函数中的某个部分。这可以通过一个静态变量来实现,该变量最初是每个线程到达该屏障时减少的总线程数。这意味着我们希望每个线程都处于休眠状态,直到最后一个线程到达。信号量的作用恰恰相反!使用信号量,每个线程将继续运行,最后一个线程(将信号量值设置为 0)将进入睡眠状态。
另一方面,条件变量是理想的。当每个线程到达屏障时,我们检查静态计数器是否为零。如果没有,我们使用条件变量等待函数将线程设置为睡眠状态。当最后一个线程到达屏障时,计数器值将递减至零,并且最后一个线程将调用条件变量信号函数,该函数将唤醒所有其他线程!
semaphore and condition variables are very similar and are used mostly for the same purposes. However, there are minor differences that could make one preferable. For example, to implement barrier synchronization you would not be able to use a semaphore.But a condition variable is ideal.
Barrier synchronization is when you want all of your threads to wait until everyone has arrived at a certain part in the thread function. this can be implemented by having a static variable which is initially the value of total threads decremented by each thread when it reaches that barrier. this would mean we want each thread to sleep until the last one arrives.A semaphore would do the exact opposite! with a semaphore, each thread would keep running and the last thread (which will set semaphore value to 0) will go to sleep.
a condition variable on the other hand, is ideal. when each thread gets to the barrier we check if our static counter is zero. if not, we set the thread to sleep with the condition variable wait function. when the last thread arrives at the barrier, the counter value will be decremented to zero and this last thread will call the condition variable signal function which will wake up all the other threads!
我在监视器同步下归档条件变量。我通常将信号量和监视器视为两种不同的同步方式。两者之间在本质上保留多少状态数据以及如何对代码进行建模方面存在差异 - 但实际上不存在任何问题可以由其中一个解决而另一个无法解决。
我倾向于针对监视器形式进行编码;在我工作的大多数语言中,这都归结为互斥体、条件变量和一些支持状态变量。但信号量也可以完成这项工作。
I file condition variables under monitor synchronization. I've generally seen semaphores and monitors as two different synchronization styles. There are differences between the two in terms of how much state data is inherently kept and how you want to model code - but there really isn't any problem that can be solved by one but not the other.
I tend to code towards monitor form; in most languages I work in that comes down to mutexes, condition variables, and some backing state variables. But semaphores would do the job too.
信号量需要预先知道初始化的计数。对于条件变量则没有这样的要求。
semaphore need to know the count upfront for initialization. There is no such requirement for condition variables.
互斥量
和条件变量
继承自信号量
。它们就像语法糖
The the
mutex
andconditional variables
are inherited fromsemaphore
.mutex
, thesemaphore
uses two states: 0, 1condition variables
thesemaphore
uses counter.They are like syntactic sugar
ConditionalVar + 互斥体 == 信号量
conditionalVar + mutex == semaphore