多线程:经典的生产者消费者算法

发布于 2024-09-30 09:36:39 字数 1638 浏览 6 评论 0原文

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

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)
    }
    up(fillCount) //the consumer may not finish before the producer.
}

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

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

semaphore mutex = 1

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


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

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

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

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

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

Something I don't get about the classical algorithm for the Producer-Consumer problem (from Wikipedia:)

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)
    }
    up(fillCount) //the consumer may not finish before the producer.
}

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

I note that both producers and consumers lock 'mutex' prior to messing with the buffer, and unlock it thereafter. If that is the case, i.e. only a single thread is accessing the buffer at any given moment, I don't really see how the above algo differs from a simple solution that entails only putting a guarding mutex over the buffer:

semaphore mutex = 1

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


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

Only thing I can think of that necessitates using the 'fillCount' and 'emptyCount' semaphores is scheduling.

Maybe the first algo is for making sure that in a state where 5 consumers are waiting on an empty buffer (zero 'fillCount'), it is assured that when a new producer comes along, it will go past its "down(emptyCount)" statement quickly and get the 'mutex' quickly.

(whereas in the other solution the consumers will needlessly get the 'mutex' only to relinquish it until the new producer gets it and inserts an item).

Am I right? Am I missing something?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

送舟行 2024-10-07 09:36:39

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

If there are no messages in the buffer, the consumer will down the mutex, check the buffer, find that it's empty, up the mutex, loop back around and immediately repeat the process. In simple terms, consumers and producers are stuck in busy loops that chew up 100% of a CPU core. This is not just a theoretical problem, either. You may well find that your computer's fan starts to spin every time you run your program.

梦回旧景 2024-10-07 09:36:39

生产者/消费者模式的漏洞概念是仅在满足特定条件时才访问共享资源(缓冲区)。并且不使用不必要的 CPU 周期以确保满足要求。

消费者:

  • 如果没有什么可以消费(=>空缓冲区),请等待。

生产者:

  • 如果缓冲区空间不足,请等待

对于两者来说,值得注意的是:

  • 等待!=旋转等待

The hole concept of the producer/consumer pattern is that the shared resource (buffer) is only accessed if certain criteria are met. And to not utilize unnecessary CPU cycles in order to make sure they are met.

Consumer:

  • Wait if there is nothing to consume (=> empty buffer).

Producer:

  • Wait if there is not enough space in the buffer.

And for both its very important to note:

  • Wait != Spin wait
鹿童谣 2024-10-07 09:36:39

发生的不仅仅是缓冲区的锁定。

第一个程序中的 fillCount 信号量用于在没有剩余内容可供消费时暂停消费者。如果没有它,您就需要不断地轮询缓冲区以查看是否有任何内容可以获取,这是相当浪费的。

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

There's more than just the locking of the buffer going on.

The fillCount semaphore in the first program is to pause the consumer(s) when there's nothing left to consume. Without it, you're constantly polling the buffer to see if there's anything to get, and that's quite wasteful.

Likewise, the emptyCount semaphore pauses the producer when the buffer is full.

人心善变 2024-10-07 09:36:39

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

There is a starving issue if more than one consumer is involved. In the latter example consumer checks for an item in a loop. But by the time he get's to try again some other consumer may "steal" his item. And next time again, and again. That is not nice.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文