我不是100%确定我遵循你的伪代码,但我会尽我所能解释如何使用信号量来管理生产者消费者进程中的堆栈。
如果您有一个跨多个线程访问的堆栈,则需要在数据正在访问时或者更具体地说是在被推送和弹出时锁定它。 (这始终是生产者 - 消费者问题的一个基本假设。)
我们首先定义一个我们将用来锁定堆栈的互斥锁。
过程的全局声明信号灯
stackAccessMutex = semaphore(1) # The "(1)" is the count
# initializer for the semaphore.
接下来,我们需要将其锁定,当我们添加或从它在我们的消费者和生产者线程删除数据。
生产者线程
dataPushBuff #Buffer containing data to be pushed to the stack.
…dataPushBuff is assigned…
stackAccessMutex.wait()
stack.push(dataPushBuff)
stackAccessMutex.signal()
Consumer线程
dataRecvBuff = nil # Defining a variable to store the pushed
# content, accessible from only within
# the Consumer thread.
stackAccessMutex.wait()
dataRecvBuff = stack.pop()
stackAccessMutex.signal()
…Consume dataRecvBuff as needed since it's removed from the stack…
到目前为止,一切都非常直截了当。生产者只会在需要时锁定堆栈。消费者也是如此。我们应该不需要另一个信号量吗?正确?没有错!
上面的场景使人产生了一个致命的假设 - 堆栈将在数据弹出之前始终使用数据进行初始化。如果消费者线程在生产者线程有机会弹出任何数据之前执行,您将在消费者线程中生成错误,因为stack.pop()
不会返回任何内容!为了解决这个问题,我们需要告知消费者数据在堆栈中可用。
首先,我们需要定义一个信号量,它可以用来表示堆栈中的数据是否存在。流程信号灯的
全球宣言,版本#2
stackAccessMutex = semaphore(1)
itemsInStack = semaphore(0)
我们初始化我们itemsInStack
在我们的堆栈,这是0 项目的数量(参见图1)。
接下来,我们需要实现我们新的信号到我们的生产者和消费者线程。首先,我们需要让生产者信号添加一个项目。我们现在更新制片人。
生产者线程版本#2
dataPushBuff
…dataPushBuff is assigned…
stackAccessMutex.wait()
stack.push(dataPushBuff)
stackAccessMutex.signal()
itemInStack.signal() #Signal the Consumer, we have data in the stack!
#Note, this call can be placed within the
#stackAccessMutex locking block, but it doesn't
#have to be there. As a matter of convention, any
#code that can be executed outside of a lock,
#should be executed outside of the lock.
现在,我们可以检查,看看是否有通过一个信号是在栈中的数据,让我们重新写我们的消费者线程。
Consumer线程版本#2
dataRecvBuff = nil # Defining a variable to store the pushed
# content, accessible from only within
# the Consumer thread.
itemsInStack.wait()
stackAccessMutex.wait()
dataRecvBuff = stack.pop()
stackAccessMutex.signal()
…Consume dataRecvBuff as needed since it's removed from the stack…
...,就是这样。正如你所看到的,有两个信号量又都是强制性(见2),因为我们需要锁定我们的堆栈时,它的访问和我们需要表明我们的消费数据可用时并锁定时,有没有什么堆栈。
希望这回答了你的问题。如果您有任何具体问题,我会更新我的回复。
从理论上讲,程序启动时,你可以 预先初始化数据的筹码。在这种情况下,您可以
应该
与 是 等于堆栈计值初始化itemsInStack
信号。但是,在这个例子的情况下,我们 假设堆栈中没有数据,也没有任何数据初始化为 。
值得一提的是,在一种情况下,您可以在理论上仅使用stackAccessMutex
即可获得 的具体情况。 考虑堆栈始终包含数据的情况。如果 堆栈是无限的,我们不需要告知我们的消费者已经添加了数据 ,因为总会有数据。但是,在 的现实中,“无限堆栈”不存在。即使这应该是你的 当前环境中的情况下,有一个在加入 的itemsInStack
信号的安全网的开销。
而且,它可能是很有诱惑力的抛出了itemsInStack
下你目前的情况计算 信号,如果以 stack.pop()
通话不会造成任何错误,如果它是不返回一个空栈上的任何 数据。
这是合理的,但不推荐。假设消费者线程正在执行循环中的 代码,则循环将连续执行堆栈消耗代码,而 不会消耗任何数据。通过使用itemsInStack
信号量,您正在暂停 线程,直到数据到达应该节省几个CPU周期。