Multithreading 生产者消费者仅使用1个附加信号量
在操作系统中,正如您在上面针对生产者-消费者的链接中所看到的,使用了两个信号量Multithreading 生产者消费者仅使用1个附加信号量,multithreading,operating-system,mutex,semaphore,Multithreading,Operating System,Mutex,Semaphore,在操作系统中,正如您在上面针对生产者-消费者的链接中所看到的,使用了两个信号量full和empty,为什么不能仅使用一个数量信号量fullEmpty 我的意思是,我们有一个二进制信号量mutex和另一个信号量fullEmpty,它最初是0,因为缓冲区中没有项目,所以为什么我们需要2个信号量(full,empty) 唯一需要更改的是wait和signal的顺序,以便fullEmpty的更新在关键部分内 有什么想法或原因吗?描述中与您的答案相关的关键陈述是“我们有一个固定大小的缓冲区。” 为了回答您
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);
消费者
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);
正如您在上面所看到的,生产者总是能够向队列中添加内容(除了持有互斥锁时),并且不需要等待消费者消费任何内容,因为缓冲区永远不会填满,即使消费者不消费项目。另一方面,在生产者生产商品之前,消费者不能消费任何东西
要回答您的问题,您需要关注“我们有一个固定大小的缓冲区”这句话。这改变了问题。由于缓冲区无法再增长到无限大小,您需要让生产者等待缓冲区已满,然后才能向缓冲区添加更多内容。这就是为什么需要第二个信号量。不仅消费者需要等待生产者,现在生产者也需要等待消费者。通过让生产者调用只有消费者调用的信号量
signal
来让生产者等待消费者
您不能只使用一个信号量,因为生产者必须等待的条件与消费者必须等待的条件不同。因为它们应该能够在不同的条件下减少和超过信号量,所以不能对两者使用相同的信号量。这是因为您必须等待两个条件:队列为空和队列为满。但经典信号量只允许您等待一个条件—等待信号量不是0 您可以使用单个同步对象来解决这个问题,但是这样的对象需要比信号量更具功能性。“有界信号量”-具有最大值的信号量应该足够了,因为它允许您阻止等待这两个条件 如何获得一个是另一个问题:
- 您可以使用互斥和条件变量构建一个
- 已经有了这个功能
- 您可以在Linux上使用(请参阅
,FUTEX_WAIT
)或在其他操作系统上使用同等软件:在FreeBSD上使用(请参阅FUTEX_WAKE
,UMTX_OP_WAIT
),在Windows 8和更高版本上使用,/UMTX_OP_WAKE
futex
接口-使用它,您可以构建比常规同步对象更强大、更高效的同步对象。今天,大多数OSES提供了一个等价的接口,甚至C++也可能在将来引入类似的内容(参见)。
几点注意:
Linux有一个可以在使用
EFD\u信号量
标志创建时充当信号量的函数,但它的最大值为0xfffffffffffe
,并且不能更改。也许有一天这个系统调用也会被扩展以支持最大值。请提供伪代码,这样我们就可以告诉您为什么它不起作用了。一个无界的P-C队列只有一个信号量和一个互斥量就可以了,但是一个有界的队列tht可以正确地与多个生产者/消费者一起工作,需要两个信号量和互斥量。谢谢你的回答。我的逻辑是,如果我们有一个计数信号量,它已经作为一个参数给定了一个固定的n
,即使对于具有“大小有界的缓冲区”的版本,为什么我们不能像第一个示例中那样只使用一个信号量full
?我的意思是说,如果full
是定义的最大容量,那么使用这个计数信号量,我们可以保证不能signal(full)
。我想我们可以在这个Java中像Semaphore full=new Semaphore(n,true)
?@BrijendarBakchodia感谢您的澄清。你的一个假设是无效的。信号量没有最大计数:您可以根据需要多次调用信号量上的signal()
:调用将始终成功,而不会阻塞(当然,我在这里忽略整数溢出,但您的问题不必担心这一点)n
只是您分配给信号量的初始计数,而不是最大计数。@BrijendarBakchodia因此,由于您无法阻止signal()
,因此必须使用两个不同的信号量。如果您可以为一个信号量设置一个最大计数,然后在达到该计数后阻塞signal()
,那么您可以只使用一个信号量。@BrijendarBakchodia当然,可以实现一个具有最大计数的信号量。这个决定取决于设计师。我在课堂上使用和学习的传统信号量没有最大值