2

关于生产者-消费者问题的经典算法我不明白的东西(来自维基百科:)

信号量互斥量 = 1
信号量填充计数 = 0
信号量 emptyCount = BUFFER_SIZE

程序生产者(){
    而(真){
        项目 = 生产项目()
        向下(空计数)
            向下(互斥体)
                putItemIntoBuffer(项目)
            向上(互斥体)
        向上(填充计数)
    }
    up(fillCount) //消费者可能不会在生产者之前完成。
}

过程消费者(){
    而(真){
        向下(填充计数)
            向下(互斥体)
                item = removeItemFromBuffer()
            向上(互斥体)
        向上(空计数)
        消费项目(项目)
    }
}

我注意到生产者和消费者都在弄乱缓冲区之前锁定“互斥锁”,然后再解锁它。如果是这种情况,即在任何给定时刻只有一个线程正在访问缓冲区,我真的看不出上述算法与只需要在缓冲区上放置一个保护互斥锁的简单解决方案有何不同:

信号量互斥量 = 1

程序生产者(){
    而(真){
        项目 = 生产项目()
        标志 = 真
        而(标志){
            向下(互斥体)
            如果(缓冲区未满()){
                putItemIntoBuffer(项目)
                标志 = 假
            }
            向上(互斥体)
        }
    }
}


过程消费者(){
    而(真){
        标志 = 真
        而(标志){
            向下(互斥体)
            if (bufferNotEmpty()) {
                item = removeItemFromBuffer()
                标志 = 假
            }
            向上(互斥体)
        }
        消费项目(项目)
    }
}

我能想到的唯一需要使用“fillCount”和“emptyCount”信号量的就是调度

也许第一个算法是为了确保在 5 个消费者正在等待一个空缓冲区(零'fillCount')的状态下,可以确保当一个新的生产者出现时,它将超过它的“down(emptyCount)”快速声明并快速获取“互斥锁”。

(而在另一种解决方案中,消费者将不必要地获得“互斥体”,只是为了放弃它,直到新的生产者获得它并插入一个项目)。

我对吗?我错过了什么吗?

4

4 回答 4

3

如果缓冲区中没有消息,消费者将关闭互斥锁,检查缓冲区,发现它是空的,打开互斥锁,循环回来并立即重复该过程。简单来说,消费者和生产者都陷入了占用 100% CPU 内核的繁忙循环中。这也不仅仅是一个理论问题。您可能会发现每次运行程序时计算机的风扇都会开始转动。

于 2010-10-30T12:07:51.400 回答
2

生产者/消费者模式的漏洞概念是共享资源(缓冲区)仅在满足某些条件时才被访问。并且不要利用不必要的 CPU 周期来确保它们得到满足。

消费者:

  • 如果没有东西可以消耗(=> 空缓冲区),请等待。

制片人:

  • 如果缓冲区中没有足够的空间,请等待

对于这两个非常重要的注意事项:

  • 等待!=旋转等待
于 2010-10-30T12:46:07.837 回答
1

不仅仅是缓冲区的锁定。

fillCount一个程序中的信号量是在没有东西可以消费时暂停消费者。没有它,您将不断地轮询缓冲区以查看是否有任何东西要获取,这非常浪费。

同样,emptyCount当缓冲区已满时,信号量会暂停生产者。

于 2010-10-30T12:11:48.427 回答
0

如果涉及多个消费者,则会出现饥饿问题。在后一个示例中,消费者在循环中检查项目。但是当他再次尝试时,其他消费者可能会“偷”他的物品。下一次,一次又一次。那不好。

于 2010-10-30T12:10:34.727 回答