什么时候应该使用信号量?什么时候应该使用条件变量(CondVar)?
答案 0 :(得分:176)
锁用于互斥。如果要确保一段代码是原子的,请锁定它。理论上你可以使用二进制信号量来做到这一点,但这是一个特例。
信号量和条件变量建立在互锁的基础上,由锁提供,用于提供对共享资源的同步访问。它们可用于类似目的。
条件变量通常用于在等待资源变为可用时避免忙等待(在检查条件时重复循环)。例如,如果你有一个线程(或多个线程)在队列为空之前无法继续进行,那么忙碌的等待方法就是执行以下操作:
//pseudocode
while(!queue.empty())
{
sleep(1);
}
这个问题是你通过让这个线程反复检查条件来浪费处理器时间。为什么不用一个可以发信号的同步变量告诉线程该资源是否可用?
//pseudocode
syncVar.lock.acquire();
while(!queue.empty())
{
syncVar.wait();
}
//do stuff with queue
syncVar.lock.release();
据推测,你将在其他地方有一个线程将事情从队列中拉出来。当队列为空时,它可以调用syncVar.signal()
来唤醒在syncVar.wait()
上睡着的随机线程(或者通常还有一个signalAll()
或broadcast()
方法来唤醒所有正在等待的线程。)
当我有一个或多个线程等待一个特定条件时(例如队列为空),我通常会使用这样的同步变量。
信号量可以类似地使用,但我认为当你拥有一个可用且基于某些整数可用事物而不可用的共享资源时,它们会被更好地使用。信号量适用于生产者/消费者的情况,生产者正在分配资源,消费者正在消费它们。
想想你是否有自动售货机。只有一台汽水机,它是一个共享资源。你有一个线程是供应商(生产者)负责保持机器库存,N个线程是买家(消费者)想要从机器中取出苏打水。机器中的苏打水数是驱动我们信号量的整数值。
每个进入苏打水机的买方(消费者)线程都会调用信号量down()
方法来获取苏打水。这将从机器中获取苏打水并将可用苏打水的数量减少1.如果有可用的苏打水,代码将继续运行down()
语句而没有问题。如果没有可用的苏打水,线程将在此处休眠,等待再次获得苏打水时的通知(当机器中有更多苏打水时)。
供应商(生产者)线程基本上是在等待苏打水机为空。当从机器取出最后一个苏打水时,供应商会收到通知(并且一个或多个消费者可能正在等待苏打水消失)。供应商将使用信号量up()
方法补充苏打水机器,每次可用的苏打水数量将增加,从而等待的消费者线程会收到更多苏打水可用的通知。
同步变量的wait()
和signal()
方法往往隐藏在信号量的down()
和up()
操作中。
当然,两种选择之间存在重叠。在许多情况下,信号量或条件变量(或条件变量集)都可以满足您的需要。信号量和条件变量都与用于维护互斥的锁定对象相关联,但随后它们在锁定之上提供额外的功能以同步线程执行。这主要取决于你找出哪一个对你的情况最有意义。
这不一定是最技术性的描述,但这就是我的理解。
答案 1 :(得分:34)
让我们了解幕后的内容。
条件变量本质上是一个等待队列,它支持阻塞等待和唤醒操作,即你可以将一个线程放入等待队列并将其状态设置为BLOCK,并获得一个线程从它出来并将其状态设置为READY。
请注意,要使用条件变量,还需要另外两个元素:
然后协议变为,
信号量本质上是一个计数器+一个互斥锁+一个等待队列。它可以在没有外部依赖的情况下使用它。您可以将其用作互斥锁或条件变量。
因此,信号量可以被视为比条件变量更复杂的结构,而后者更轻巧,更灵活。
答案 2 :(得分:15)
信号量可用于实现对变量的独占访问,但它们用于同步。另一方面,互斥锁具有与互斥完全相关的语义:只有锁定资源的进程才能解锁。
不幸的是,您无法与互斥锁实现同步,这就是我们拥有条件变量的原因。另请注意,使用条件变量,您可以使用广播解锁在同一时刻解锁所有等待的线程。信号量无法做到这一点。
答案 3 :(得分:6)
信号量和条件变量非常相似,主要用于相同的目的。但是,有一些细微差别可能使一个人更可取。例如,要实现屏障同步,您将无法使用信号量。但条件变量是理想的。
屏障同步是指您希望所有线程都等到每个人都到达线程函数中的某个部分。这可以通过使用静态变量来实现,该静态变量最初是每个线程在到达该障碍时递减的总线程的值。这意味着我们希望每个线程一直睡到最后一个线程到达。信号量就会完全相反!使用信号量,每个线程将继续运行,最后一个线程(将信号量值设置为0)将进入休眠状态。
另一方面,条件变量是理想的。当每个线程到达屏障时,我们检查我们的静态计数器是否为零。如果没有,我们使用条件变量wait函数将线程设置为sleep。当最后一个线程到达屏障时,计数器值将减少到零,最后一个线程将调用条件变量信号函数,它将唤醒所有其他线程!
答案 4 :(得分:1)
我在监视器同步下提交条件变量。我一般认为信号量和监视器是两种不同的同步方式。两者之间在数据本身保存的程度以及您希望如何对代码建模方面存在差异 - 但实际上没有任何问题可由一个而不是另一个解决。
我倾向于对监视器表单进行编码;在我工作的大多数语言中,归结为互斥体,条件变量和一些支持状态变量。但是信号量也会起作用。
答案 5 :(得分:-1)
mutex
和conditional variables
继承自semaphore
。
mutex
,semaphore
使用两种状态:0、1 condition variables
,semaphore
使用计数器。它们就像语法糖
答案 6 :(得分:-1)
conditionalVar + mutex == 信号量