条件variables与信号量
何时应该使用信号量,何时应该使用条件variables(CondVar)?
锁用于相互排斥。 当你想确保一段代码是primefaces的时候,在它周围放一个锁。 理论上你可以使用二进制信号来做到这一点,但这是一个特例。
信号量和条件variablesbuild立在由锁提供的互斥之上,并用于提供对共享资源的同步访问。 它们可以用于类似的目的。
一个条件variables通常用于避免在等待资源变得可用时忙于等待(在检查条件时重复循环)。 例如,如果你有一个线程(或多个线程)不能继续向前,直到一个队列是空的,忙碌的等待方法就是做一些事情:
//pseudocode while(!queue.empty()) { sleep(1); }
这个问题是通过让这个线程重复检查条件来浪费处理器时间。 为什么不能有一个同步variables,可以发信号告诉线程资源可用?
//pseudocode syncVar.lock.acquire(); while(!queue.empty()) { syncVar.wait(); } //do stuff with queue syncVar.lock.release();
据推测,你将有一个线程在其他地方,把事情拉出队列。 当队列为空时,它可以调用syncVar.signal()
唤醒在syncVar.wait()
上睡着的随机线程(或者通常还有一个signalAll()
或broadcast()
方法来唤醒所有线程正在等待)。
当我有一个或多个线程等待一个特定的条件(例如队列为空)时,我通常使用同步variables。
信号量可以类似地使用,但是我认为当你有一个可用的共享资源时,他们会更好地被使用。 信号量对生产者分配资源和消费者消费的生产者/消费者情况是有利的。
想想你是否有一台苏打水自动售货机。 只有一个苏打水机,这是一个共享资源。 你有一个供应商(生产者)的线程负责保持机器的库存,而N个线程是买方(消费者)谁想要苏打水从机器。 机器中苏打水的数量是驱动我们的信号量的整数值。
每一个进入汽水机的买方(消费者)线程都会调用信号量down()
方法来获取汽水。 这将从机器中汲取苏打水,并将可用苏打水的数量减1。如果有可用苏打水,代码将继续运行down()
语句而没有问题。 如果没有苏打水可用,则线程将在此睡眠,等待再次提供苏打水的时间(当机器中有更多苏打水时)。
供应商(生产商)线程将基本上等待苏打水机器是空的。 最后一次苏打水从机器中取出时,供应商会收到通知(并且一个或多个消费者有可能正在等待苏打水)。 供应商将通过信号量up()
方法重新补充苏打水机器,每次都会增加苏打水的可用数量,从而等待的消费者线程会得到通知,提供更多的苏打水。
同步variables的wait()
和signal()
方法往往隐藏在信号量的down()
和up()
操作中。
当然这两个select之间有重叠。 信号量或条件variables(或一组条件variables)都可以用于您的目的。 信号量和条件variables都与一个锁对象相关联,它们用来保持互斥,但是它们在锁的顶部提供了额外的function来同步线程执行。 主要由你来弄清楚哪一个对你的情况最有意义。
这不一定是最具技术性的描述,但这就是我脑中的意义所在。
让我们来揭示一下底下的东西。
条件variables本质上是一个等待队列 ,它支持阻塞等待和唤醒操作,也就是说,你可以把一个线程放到等待队列中,并把它的状态设置为BLOCK,然后从它得到一个线程并将其状态设置为READY。
请注意,要使用条件variables,需要两个其他元素:
- 一个条件(通常通过检查一个标志或一个计数器来实现)
- 一个保护条件的互斥体
协议然后成为,
- 获得互斥体
- 检查条件
- 如果条件为真,则阻止并释放互斥体,否则释放互斥体
信号量本质上是一个计数器+互斥量+等待队列。 而且可以按原样使用,无需外部依赖。 您可以使用它作为互斥或作为条件variables。
因此,信号量可以被视为比条件variables更复杂的结构,而后者则更加轻便和灵活。
信号量可以用来实现对variables的独占访问,但它们是用来同步的。 另一方面,互斥体具有与互斥严格相关的语义:只有locking资源的过程被允许解锁。
不幸的是,你不能与互斥体实现同步,这就是为什么我们有条件variables。 另外请注意,通过使用条件variables,您可以使用广播解锁在同一时刻解锁所有正在等待的线程。 这不能用信号量来完成。
信号量和条件variables非常相似,主要用于相同的目的。 但是,有一些细微的差别,可以使一个更好的。 例如,要实现障碍同步,您将无法使用信号量。但条件variables是理想的。
当你希望所有的线程都等待,直到每个人都到达了线程函数中的某个部分时,屏障同步就是这样。 这可以通过具有静态variables来实现,该静态variables最初是每个线程到达该屏障时递减的总线程的值。 这意味着我们希望每个线程都睡觉,直到最后一个线程到达。信号量将完全相反! 有一个信号量,每个线程将继续运行,最后一个线程(将信号量值设置为0)将进入hibernate状态。
另一方面,条件variables是理想的。 当每个线程到达屏障时,我们检查我们的静态计数器是否为零。 如果没有,我们使用条件variables等待函数将线程设置为睡眠。 当最后一个线程到达屏障时,计数器值将递减到零,最后一个线程将调用条件variables信号函数,唤醒所有其他线程!
我在监视器同步下logging条件variables。 我通常将信号量和监视器视为两种不同的同步方式。 就状态数据固有地保存多less以及如何对代码进行build模而言,两者之间存在差异 – 但实际上没有任何问题可以由一个解决,而不是由另一个解决。
我倾向于对监视器forms编码; 在大多数语言中,我的工作涉及到互斥体,条件variables和一些支持状态variables。 但是信号量也可以做这个工作。