セマフォの使用

セマフォは、同期プリミティブの一種で、ゲートのように、その値を減少することでコード内でのスレッドの実行を禁止したり、その値を増加させることでそのコード内での (他のスレッドの) 実行を許可したりします。セマフォは、ミューテックスと似ているので、ミューテックスのかわりに使用できます。

次のコードでは、セマフォの使用例を示します。

 Working-Storage Section.
 01  data-semaphore     usage semaphore-pointer.
 01  data-value         pic x(4) comp-x value 0.

* Initialization code executed while in single threaded mode
     open data-semaphore
     set data-semaphore up by 1      *> Initialize as raised

* Add change data-value, this is a critical section
     set data-semaphore down by 1
     add 1 to data-value
*  Allow other thread to pass semaphore
     set data-semaphore up by 1      

OPEN 文の直後に、セマフォが 1 だけ増加することに注意してください。これにより、最初のセマフォの獲得は成功しますが、それ以降の獲得はそのセマフォが再度解放されるまではすべてブロックされます。

セマフォは、ミューテックスほど効果的ではありませんが、より柔軟性があります。1 つのスレッドがセマフォを単に解放するのみで、他のスレッドはその解放されたセマフォを獲得できます。ミューテックスと対比してみてください。ミューテックスの場合は、常に解放される前に獲得する必要があります。ミューテックスの獲得操作と解放操作は同じスレッド内で行う必要があります。セマフォは、あるスレッドから別のスレッドへのシグナルを提供します。

次のコードでは、2 つのスレッド間のハンドシェイクを確立するために、異なる 2 つのセマフォを使用します。このハンドシェイクにより、一方のスレッドは新規データ値の生成を通知し、他方のスレッドはそのデータ値の使用を通知できます。

 Working-Storage Section.
 01  produced-semaphore         usage semaphore-pointer.
 01  data-value                 pic x(4) comp-x value 0.
 01  consumed-semaphore         usage semaphore-pointer.

* Initialization code executed while in single threaded mode
     open produced-semaphore
     open consumed-semaphore
     set consumed-semaphore up by 1

* This code is executed once to produce a data value
     set consumed-semaphore down by 1
     add 10 to data-value
*  Signal that data value has changed
     set produced-semaphore up by 1 

* Another thread, waiting for the data-value to change,  
* executes this code once as well.
     set produced-semaphore down by 1
     display data-value
*  Signal data value used
     set consumed-semaphore up by 1 

この例では、作成側と使用側の問題として知られる、もう 1 つの一般的な同期問題を示しています。最も単純な作成者/使用者間の問題は、データを生成する 1 つのスレッドとそのデータを使用する もう 1 つのスレッドがある場合に、使用側スレッドの実行中は処理対象のデータが常に存在するように、作成スレッドと使用スレッド間で実行の同期を取る必要があるということです。

この例のセマフォは、解放されていないセマフォ数を数え、多数のスレッドがセマフォを獲得しブロック解除を行うようにします。セマフォをこのように数えると、使用スレッドがデータ値を獲得するまでの間、ブロックして待機する前に、作成スレッドは複数のデータの値 (通常は配列) を生成できます。

次のコードは、作成側と使用側の組の簡単な例を示します。作成側は、データ表がいっぱいになるまでデータの値を作成できます。データ表がいっぱいになると、作成側は値が使用されるまで新たに値を作成しません。

 Working-Storage Section.
 78  table-size    value 20.
 01  produced-semaphore   usage semaphore-pointer.
 01  filler.
     05  table-data-value pic x(4) comp-x 
             occurs table-size times value 0.
 01  consumed-semaphore   usage semaphore-pointer.

 Local-Storage Section.
 01  table-i     pic x(4) comp-x.

* Initialization code executed while in single threaded mode
     open produced-semaphore
     open consumed-semaphore
*  Start raised 20 times
     set consumed-semaphore up by table-size 

* Producer thread
     move 1 to table-i
     perform until 1 = 0
         set consumed-semaphore down by 1
         add table-i to table-data-value(table-i)
         set produced-semaphore up by 1
         add 1 to table-i
         if  table-i > table-size
             move 1 to table-i
         end-if
     end-perform.

* Consumer thread
     move 1 to table-i
     perform until 1 = 0
         set produced-semaphore down by 1
         display 'Current produced value is' 
                 table-data-value(table-i)
         set consumed-semaphore up by 1
         add 1 to table-i
         if  table-i > table-size
             move 1 to table-i
         end-if
     end-perform.