私はあなたの擬似コードに従うことを100%保証しているわけではありませんが、セマフォを使用してProducer-consumerプロセス内からスタックを管理する方法を説明するために最善を尽くします。
複数のスレッドにまたがってアクセスされているスタックがある場合、データがアクセスされているとき、具体的にはプッシュおよびポップされているときにロックする必要があります。これは常にプロデューサ - 消費者問題の根本的な前提です。
まず、スタックをロックするために使用するミューテックスを定義します。私たちは消費者と生産者のスレッドでそれからのデータを追加または削除された場合
プロセスのグローバル宣言セマフォ
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()
消費者スレッド
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)
我々は(1を参照)0 である私達のスタック内のアイテムの数に私たちのitemsInStack
を初期化します。
次に、新しいセマフォをProducerおよびConsumerスレッドに実装する必要があります。まず、アイテムが追加されたことをProducerの信号で確認する必要があります。今プロデューサーを更新しましょう。
プロデューサースレッド、我々は、スタック内のデータがセマフォを経由してあるかどうかをチェックできるようになったので、バージョン#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.
、私たちの消費者のスレッドを再書いてみましょう。
消費者スレッド、バージョン#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つのセマフォがあり、両方ともの場合はとにスタックをロックする必要があるため、両方のセマフォが必須です(2参照)。データが利用できるときにコンシューマーに通知し、スタック
ご質問にお答えします。特定の質問がある場合は、私の回答を更新します。
処理が開始されると、理論的には、データが事前に初期化し、あなたのスタックを ことができます。この場合、
は
になります。 は、 の値をスタックカウントと同じ値でitemsInStack
セマフォを初期化する必要があります。ただし、この例の場合、 はスタックにデータがないと仮定しており、 は初期化しません。
それは1の下で、特定の状況はあなたが 理論的にはちょうどstackAccessMutex
を使用して離れて得ることができることを言及する価値があります。 スタックに常にデータが含まれている場合を考えてみましょう。 スタックが無限である場合、常にデータが存在するため、データ が追加されたことをコンシューマに通知する必要はありません。しかし、 では、実際には「無限のスタック」は存在しません。たとえそれが現在の状況で である場合でも、 itemsInStack
セマフォのセーフティネットを追加する際のオーバーヘッドはありません。
また、空のスタック上の任意の データを返さないとしたら stack.pop()
の呼び出しがエラーを起こさない現在の状況下であれば セマフォを数えるitemsInStack
を捨てるためにしたくても良いです。
これは可能ですが、お勧めしません。コンシューマスレッドがループ上で コードを実行していると仮定すると、ループはスタック消費コードを継続的に実行し、 は消費するデータがありません。itemsInStack
セマフォを使用すると、データが到着するまでに スレッドを一時停止します。これにより、CPUサイクルが数回節約されます。