Producer Consumer 仅用 1 个信号量同步

Producer Consumer synchronization with only 1 semaphore

Producer-Consumer problem中,标准方案使用3个信号量。
但是,我想知道我们是否可以只使用 1 个信号量:

semaphore mutex = 1

procedure producer() {
    while (true) {
        down(mutex)
        if (bufferNotFull()) {
            item = produceItem()
            putItemIntoBuffer(item)
        }
        up(mutex)
    }
}

procedure consumer() {
    while (true) {
        down(mutex)
        if (bufferNotEmpty()) {
            item = removeItemFromBuffer()
            consumeItem(item)
        }
        up(mutex)
    }
}

这个解决方案和标准解决方案一样好吗??

供参考的标准溶液:

semaphore mutex = 1
semaphore fillCount = 0
semaphore emptyCount = BUFFER_SIZE

procedure producer() {
    while (true) {
        item = produceItem()
        down(emptyCount)
        down(mutex)
        putItemIntoBuffer(item)
        up(mutex)
        up(fillCount)
    }
}

procedure consumer() {
    while (true) {
        down(fillCount)
        down(mutex)
        item = removeItemFromBuffer()
        consumeItem(item)
        up(mutex)
        up(emptyCount)
    }
}

“标准解决方案”不必使用 3 个信号量。即使是您链接的维基百科文章也有一个双信号量解决方案,适用于只有一个生产者和一个消费者的情况:

semaphore fillCount = 0; // items produced
semaphore emptyCount = BUFFER_SIZE; // remaining space

procedure producer() 
{
    while (true) 
    {
        item = produceItem();
        down(emptyCount);
        putItemIntoBuffer(item);
        up(fillCount);
    }
}

procedure consumer() 
{
    while (true) 
    {
        down(fillCount);
        item = removeItemFromBuffer();
        up(emptyCount);
        consumeItem(item);
    }
}

具有单个信号量的解决方案不是很好,因为当您真正想要时它们只给您一个等待条件——“免费等待 space”和“等待某个项目”。你不能用一个信号量做这两件事。

无论如何,您的解决方案并没有错,只是效率很低,因为在任何给定时刻,只有一个生产者或一个消费者可以 运行。这本质上是单线程代码,只有锁和线程之间的上下文切换。所以它比实际的单线程代码效率更低。

还有一件事 - 物品生产和消耗通常不是关键部分的一部分。当项目正在被消费或生产时,另一个线程可以 运行。只有在使用缓冲区时才需要互斥。