生产者消费者只使用 1 个额外的信号量
Producer consumer using only 1 additional semaphore
Solution traditional to producer-consumer
在操作系统中,正如您在上面的 link 中看到的生产者消费者,使用了两个信号量 full
和 empty
,为什么 没有可能只使用一个数量信号量fullEmpty
。
我的意思是,我们有一个二进制信号量mutex
和另一个信号量fullEmpty
,最初是0
,因为缓冲区中没有项目,所以我们为什么需要 2 个信号量(full
、empty
)?
唯一的问题是wait
和signal
的顺序需要改变,以便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" - 具有最大值的信号量应该足够了,因为它允许您阻止等待这两个条件。
如何获得是另一个问题:
- 您可以使用互斥锁和条件变量构建一个。
- Window's semaphore 已经有这个功能了。
- 您可以使用
futex
on Linux (see FUTEX_WAIT
, FUTEX_WAKE
) or equivalents on other OSes: on FreeBSD use _umtx_op
(see UMTX_OP_WAIT
, UMTX_OP_WAKE
), on Windows 8 and newer use WaitOnAddress
, WakeByAddressSingle
/WakeByAddressAll
.
我建议您熟悉 futex
界面 - 使用它您可以构建比常规对象更强大、更高效的同步对象。今天,大多数操作系统都提供了等效的接口,甚至 C++ 将来也可能会引入类似的东西(参见 std::synchronic<T>
)。
几点注意事项:
Linux 有 eventfd
,当用 EFD_SEMAPHORE
标志创建时可以充当信号量,但它的最大值为 0xfffffffffffffffe
并且不能更改。也许有一天这个系统调用也会扩展到支持最大值。
Solution traditional to producer-consumer
在操作系统中,正如您在上面的 link 中看到的生产者消费者,使用了两个信号量 full
和 empty
,为什么 没有可能只使用一个数量信号量fullEmpty
。
我的意思是,我们有一个二进制信号量mutex
和另一个信号量fullEmpty
,最初是0
,因为缓冲区中没有项目,所以我们为什么需要 2 个信号量(full
、empty
)?
唯一的问题是wait
和signal
的顺序需要改变,以便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" - 具有最大值的信号量应该足够了,因为它允许您阻止等待这两个条件。
如何获得是另一个问题:
- 您可以使用互斥锁和条件变量构建一个。
- Window's semaphore 已经有这个功能了。
- 您可以使用
futex
on Linux (seeFUTEX_WAIT
,FUTEX_WAKE
) or equivalents on other OSes: on FreeBSD use_umtx_op
(seeUMTX_OP_WAIT
,UMTX_OP_WAKE
), on Windows 8 and newer useWaitOnAddress
,WakeByAddressSingle
/WakeByAddressAll
.
我建议您熟悉 futex
界面 - 使用它您可以构建比常规对象更强大、更高效的同步对象。今天,大多数操作系统都提供了等效的接口,甚至 C++ 将来也可能会引入类似的东西(参见 std::synchronic<T>
)。
几点注意事项:
Linux 有 eventfd
,当用 EFD_SEMAPHORE
标志创建时可以充当信号量,但它的最大值为 0xfffffffffffffffe
并且不能更改。也许有一天这个系统调用也会扩展到支持最大值。