Solution traditional to producer-consumer
在操作系统中,正如您在上面的生产者消费者链接中看到的那样,使用了两个信号量full
和empty
,为什么不能仅使用一个数量信号量fullEmpty
来执行此操作。
我的意思是,我们有一个二进制信号量mutex
和另一个信号量fullEmpty
,最初是0
,因为缓冲区中没有项目,为什么我们需要2个信号量(full
,empty
)?
唯一的事情是wait
和signal
的顺序需要改变,以便fullEmpty
的更新在关键部分。
有什么想法或原因吗?
描述中与您的答案相关的关键声明是“我们有一个固定大小的缓冲区”。
为了回答你的问题,我们首先假设缓冲区可以根据需要扩展以适应尽可能多的项目,换句话说,缓冲区可以增长到无限大小。在这种情况下,生产者和消费者之间需要进行的唯一同步(除了锁定互斥锁以确保您不会破坏关键部分中的项目)将确保消费者仅在消费者生产后使用物品。制片人。你可以用一个互斥量和一个信号量来解决这个问题。这是一些代码,我从你分享的链接中借用和更改了这些代码:
制片人
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
。
您不能只使用一个信号量执行此操作,因为生产者必须等待的条件与消费者必须等待的条件不同。由于它们应该能够在不同条件下递减并超过信号量,因此不能对两者使用相同的信号量。
这是因为您必须等待2个条件:队列为空且队列已满。但是经典信号量允许您只等待一个条件 - 等到信号量不为0。
您可以使用单个同步对象解决此问题,但此类对象需要比信号量更多的功能。 “有界信号量” - 具有最大值的信号量应该足够,因为它允许您阻止等待这两个条件。
如何获得一个是另一个问题:
futex
(参见FUTEX_WAIT
,FUTEX_WAKE
)或其他操作系统的等价物:在FreeBSD上使用_umtx_op
(参见UMTX_OP_WAIT
,UMTX_OP_WAKE
),在Windows 8上使用WaitOnAddress
,WakeByAddressSingle
/ WakeByAddressAll
。我建议你熟悉futex
界面 - 用它你可以构建比常规界面更强大,更有效的同步对象。今天,大多数操作系统提供了等效的接口,甚至C ++也可能会在未来引入类似的东西(参见std::synchronic<T>
)。
几点说明:
Linux有eventfd
,它可以在使用EFD_SEMAPHORE
标志创建时充当信号量,但它具有最大值0xfffffffffffffffe
并且无法更改。也许有一天,这个系统调用将扩展到支持最大值。