Solution traditional to producer-consumer
在操作系统中,如您在上面的生产者消费者链接中所看到的,使用了两个信号量full
和empty
,为什么不可能仅使用一个数量的信号量fullEmpty
。
我的意思是,我们有一个二进制信号量mutex
和另一个信号量fullEmpty
,最初是0
,因为缓冲区中没有项目,所以为什么需要 2个信号灯(full
,empty
)?
唯一的事情是wait
和signal
的顺序需要更改,以便fullEmpty
的更新在关键部分内。
有什么想法或理由吗?
答案 0 :(得分:1)
说明中与您的答案相关的主要说明是“我们有固定大小的缓冲区。”
为了回答您的问题,我们首先假定缓冲区可以扩展以容纳所需的所有项目,换句话说,缓冲区可以增长到无限大小。在这种情况下,生产者和使用者之间唯一需要进行的同步(除了锁定互斥锁以确保您不会破坏关键部分中的项目),还要确保使用者仅在之后食用
制作人
do {
//produce an item
wait(mutex);
//place in buffer
signal(mutex);
signal(full);
} while (true);
消费者
do {
wait(full);
wait(mutex);
//remove item from buffer
signal(mutex);
//consume item
} while (true);
正如您在上面看到的,生产者总是能够将事物添加到队列中(除了持有互斥锁的时间),并且不需要等待使用者消耗任何东西,因为缓冲区永远不会填满,甚至如果消费者不消费物品。另一方面,在生产者生产商品之前,消费者无法消费任何东西。
要回答您的问题,您需要关注以下语句:“我们有固定大小的缓冲区”。这改变了问题。由于缓冲区不再能够增长到无限大小,因此您需要让生产者在缓冲区已满时等待,然后他们才能向缓冲区添加更多内容。这就是为什么您需要第二个信号灯的原因。消费者不仅需要等待生产者,而且现在生产者也需要等待消费者。通过让生产者在仅消费者调用wait
的信号量上调用signal
,可以使生产者等待消费者。
您不能仅使用一个信号量来执行此操作,因为生产者必须等待的条件与消费者必须等待的条件不同。由于它们应该能够在不同的条件下递减并越过信号量,因此您不能对两者使用相同的信号量。
答案 1 :(得分:0)
这是因为必须等待两个条件:队列为空,队列已满。但是经典信号量只允许您等待一种情况-等待信号量不为0。
您可以使用单个同步对象解决此问题,但是该对象需要比信号量更完整的功能。 “有界信号量”-具有最大值的信号量就足够了,因为它可以让您阻止等待两种情况。
如何获得另一个问题:
FUTEX_WAIT
,FUTEX_WAKE
)上使用futex
或在其他操作系统上使用等效命令:在FreeBSD上请使用_umtx_op
(请参阅UMTX_OP_WAIT
,{ {1}}),在Windows 8和更高版本上使用WaitOnAddress
,WakeByAddressSingle
/ WakeByAddressAll
。我建议您熟悉UMTX_OP_WAKE
接口-与常规接口相比,它可以构建更强大,更高效的同步对象。今天,大多数操作系统都提供了等效的接口,甚至C ++将来都可能引入类似的内容(请参见std::synchronic<T>
)。
一些注意事项:
Linux具有eventfd
,当使用futex
标志创建时,Linux可以充当信号灯,但是它的最大值为EFD_SEMAPHORE
,并且不能更改。也许有一天这个系统调用也将扩展为支持最大值。