生产者消费者只使用 1 个额外的信号量

Producer consumer using only 1 additional semaphore

Solution traditional to producer-consumer

在操作系统中,正如您在上面的 link 中看到的生产者消费者,使用了两个信号量 fullempty,为什么 没有可能只使用一个数量信号量fullEmpty

我的意思是,我们有一个二进制信号量mutex和另一个信号量fullEmpty,最初是0,因为缓冲区中没有项目,所以我们为什么需要 2 个信号量(fullempty)?

唯一的问题是waitsignal的顺序需要改变,以便fullEmpty的更新在临界区内。

有什么想法或原因吗?

描述中与您的答案相关的关键语句是 "We have a buffer of fixed size."

为了回答您的问题,我们首先假设缓冲区可以根据需要扩展以容纳任意数量的项目,或者换句话说,缓冲区可以增长到无限大小。在这种情况下,生产者和消费者之间唯一需要发生的同步(除了锁定互斥锁以确保您不会损坏关键部分中的项目)将确保消费者仅在 之后消费项目 它们是由生产者生产的。你可以只用一个互斥量和一个信号量来解决这个问题。这是一些代码,我从您分享的 link 中借用并更改了这些代码:

制作人

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);

正如你在上面看到的,生产者总是能够向队列中添加东西(除了持有互斥锁的时候)并且不需要等待消费者消费任何东西,因为缓冲区永远不会填满,即使消费者不消费物品。另一方面,在生产者生产商品之前,消费者不能消费任何东西。


要回答你的问题,你需要关注语句,"We have a buffer of fixed size."这改变了问题。由于缓冲区不再能够增长到无限大小,您需要让生产者在缓冲区已满时等待,然后才能向缓冲区添加更多内容。这就是为什么你需要第二个信号量。不仅消费者需要等待生产者,现在生产者也需要等待消费者。您可以让生产者在只有消费者调用 signal 的信号量上调用 wait 来让生产者等待消费者。

你不能只用一个信号量来做到这一点,因为生产者必须等待的条件与消费者必须等待的条件不同。由于它们应该能够在不同条件下递减和超过信号量,因此您不能对两者使用相同的信号量。

这是因为您必须等待两个条件:队列为空和队列已满。但是经典信号量只允许你等待一个条件——等到信号量不为 0。

可以使用单个同步对象解决此问题,但此类对象需要比信号量功能更全面。 "Bounded semaphore" - 具有最大值的信号量应该足够了,因为它允许您阻止等待这两个条件。

如何获得是另一个问题:

我建议您熟悉 futex 界面 - 使用它您可以构建比常规对象更强大、更高效的同步对象。今天,大多数操作系统都提供了等效的接口,甚至 C++ 将来也可能会引入类似的东西(参见 std::synchronic<T>)。


几点注意事项:

Linux 有 eventfd ,当用 EFD_SEMAPHORE 标志创建时可以充当信号量,但它的最大值为 0xfffffffffffffffe 并且不能更改。也许有一天这个系统调用也会扩展到支持最大值。