multithreading 条件变量与信号量

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3513045/
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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-10 01:10:29  来源:igfitidea点击:

Conditional Variable vs Semaphore

multithreadingoperating-systemsynchronizationsemaphoremutual-exclusion

提问by doron

When should one use a semaphore and when should one use a conditional variable (CondVar) ?

什么时候应该使用信号量,什么时候应该使用条件变量(CondVar)?

回答by Brent Writes Code

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:

条件变量通常用于避免在等待资源可用时忙等待(检查条件时重复循环)。例如,如果您有一个线程(或多个线程)在队列为空之前无法继续前进,那么忙等待方法就是执行以下操作:

//pseudocode
while(!queue.empty())
{
   sleep(1);
}

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?

这样做的问题是,让这个线程反复检查条件,是在浪费处理器时间。为什么不用一个同步变量来通知线程资源可用呢?

//pseudocode
syncVar.lock.acquire();

while(!queue.empty())
{
   syncVar.wait();
}

//do stuff with queue

syncVar.lock.release();

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 on syncVar.wait()(or there's usually also a signalAll()or broadcast()method to wake up all the threads that are waiting).

据推测,您将在其他地方有一个线程将事物从队列中拉出。当队列为空时,它可以调用syncVar.signal()唤醒一个处于睡眠状态的随机线程syncVar.wait()(或者通常还有一个signalAll()orbroadcast()方法来唤醒所有正在等待的线程)。

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.

想想你是否有汽水自动售货机。只有一台汽水机,而且是共享资源。您有一个线程是负责保持机器库存的供应商(生产者)和 N 个线程是希望从机器中取出苏打水的买家(消费者)。机器中苏打水的数量是驱动我们信号量的整数值。

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 the down()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).

来到汽水机的每个买家(消费者)线程都会调用信号量down()方法来获取汽水。这将从机器中获取苏打水并将可用苏打水的计数减 1。如果有苏打水可用,代码将继续运行,down()而不会出现问题。如果没有可用的苏打水,线程将在此处休眠,等待在苏打水再次可用时收到通知(当机器中有更多苏打水时)。

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.

供应商(生产者)线程本质上将等待汽水机为空。当最后一瓶汽水从机器中取出时,供应商会收到通知(并且一个或多个消费者可能正在等待取出汽水)。供应商将使用 semaphoreup()方法补货苏打水机,可用苏打水的数量每次都会增加,因此等待的消费者线程将收到更多苏打水可用的通知。

The wait()and signal()methods of a synchronization variable tend to be hidden within the down()and up()operations of the semaphore.

同步变量的wait()signal()方法往往隐藏在信号量的down()up()操作中。

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.

这不一定是最具技术性的描述,但这就是我脑海中的意义。

回答by cucufrog

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.

条件变量本质上是一个等待队列,它支持阻塞等待和唤醒操作,即您可以将一个线程放入等待队列并将其状态设置为 BLOCK,然后从中取出一个线程并将其状态设置为 READY。

Note that to use a conditional variable, two other elements are needed:

请注意,要使用条件变量,还需要另外两个元素:

  • a condition (typically implemented by checking a flag or a counter)
  • a mutex that protects the condition
  • 条件(通常通过检查标志或计数器来实现)
  • 保护条件的互斥锁

The protocol then becomes,

然后协议变成,

  1. acquire mutex
  2. check condition
  3. block and release mutex if condition is true, else release mutex
  1. 获取互斥量
  2. 检查条件
  3. 如果条件为真,则阻止并释放互斥锁,否则释放互斥锁

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.

因此,信号量可以被视为比条件变量更复杂的结构,而后者更轻量和灵活。

回答by Dacav

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.

不幸的是,您无法使用互斥锁实现同步,这就是我们有条件变量的原因。另请注意,使用条件变量,您可以使用广播解锁在同一时刻解锁所有等待线程。这不能用信号量来完成。

回答by Danielle

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.

屏障同步是指您希望所有线程都等待,直到每个人都到达线程函数中的某个部分。这可以通过具有一个静态变量来实现,该变量最初是每个线程在到达该屏障时递减的总线程值。这意味着我们希望每个线程都休眠,直到最后一个线程到达。信号量会做完全相反的事情!使用信号量,每个线程将继续运行,最后一个线程(将信号量值设置为 0)将进入睡眠状态。

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!

另一方面,条件变量是理想的。当每个线程到达屏障时,我们检查我们的静态计数器是否为零。如果没有,我们使用条件变量等待函数将线程设置为休眠。当最后一个线程到达屏障时,计数器值将递减为零,最后一个线程将调用条件变量信号函数,该函数将唤醒所有其他线程!

回答by Justin R

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.

我倾向于对监视器形式进行编码;在我使用的大多数语言中,这归结为互斥体、条件变量和一些支持状态变量。但是信号量也可以完成这项工作。