FreeBASIC マニュアルのトップに戻る

FreeBASIC ProPgMtConditionalVariables

目次→教本→プログラマーのための案内Conditional Variables←オリジナル・サイト

条件変数 左にメニュー・フレームが表示されていない場合は、ここをクリックして下さい

←リンク元に戻る プログラム開発関連に戻る

条件変数 を、作成、待機/シグナル、破棄する、組み込み手続き。

序文:
条件変数は、スレッドが(CPUサイクルを消費せずに)イベント発生まで待機できるメカニズムです。
いくつかのスレッドは、いくつかの他のスレッドがこの条件変数を通知するまで(つまり、通知を送信するまで)、条件変数で待機する場合があります。
この時点で、この条件変数を待機しているスレッドの1つが起動し、イベントに対応できます。
この変数で、ブロードキャスト・メソッドを使うことにより、この条件変数で待機しているすべてのスレッドを、起動することもできます。

条件変数はロックを提供しません。
したがって、この条件変数にアクセスするときに、必要なロックを提供するには、条件変数とともにミューテックスを使う必要があります。

条件付き変数機能(およびミューテックス機能)は、切り離されたスレッドでも、完全に使えます(そのハンドラーだけが識別子によってアクセスできなくなります)。

条件変数の 作成 / 破棄
CondCreate は条件変数を作成し、条件変数を破棄するときに参照される、ハンドル識別子を返します。
CondCreate で作成された条件変数は、不要になったとき、または CondDestroy でプログラムの終了前に、破棄する必要があります(OS でのリソースのリークを避けるため)。

Create
- 構文:
- 用法:
conditionalid = CondCreate
- 戻り値:
作成された条件変数への、any ptr ハンドル(条件付き)、または、失敗した場合は NULL ポインター (0) 。

Destroy
- 構文:
declare sub CondDestroy ( byval conditionalid as any ptr )
- 用法:
CondDestroy ( conditionalid )
- パラメータ:
conditionalid
破棄する条件変数の any ptr ハンドル。

説明
CondDestroy は、条件変数を破棄し、保持している可能性のあるリソースを解放します。
CondDestroy への入り口で、条件変数を待機しているスレッドはありません。

条件変数の待機/シグナル
条件変数のメカニズムにより、スレッドは実行を一時停止し、何らかの条件が満たされるまで、プロセッサを手放します。

CondWait は、何らかの条件が真になるまで、現在のスレッドの実行を停止します。
CondSignal は、条件で待機している 1つのスレッドを再起動できます。一方、CondBroadCast は、条件で待機しているすべてのスレッドを再起動できます。

Wait-for
- 構文:
declare sub CondWait ( byval conditionalid as any ptr, byval mutexid as any ptr )
- 用法:
CondWait ( conditionalid, mutexid )
- パラメータ:
conditionalid
条件変数のハンドル識別子。
mutexid
この条件変数に関連付けられた、ミューテックスのハンドル識別子。
条件をテストして、CondWait を呼び出すときに、ロックする必要があります。

Signal
- 構文:
declare sub CondSignal ( byval conditionalid as any ptr )
or
declare sub CondBroadCast ( byval conditionalid as any ptr )
- 用法:
CondSignal ( conditionalid )
or
CondBroadCast ( conditionalid )
- パラメータ:
conditionalid
シグナルを送信する条件変数の、any ptr ハンドル。

説明
CondCreate で条件変数が作成され、スレッドが開始されると、スレッドのうちの1つ以上(メインプログラムを実行する暗黙のメインスレッドを含む)を、CondWait によって条件の状態まで待機させることができます。
これらのスレッドは、別のスレッドが CondSignal によって待機中のスレッドの一つが再起動できることを通知するまで停止します。
CondBroadCast を使うと、条件付きで待っている全てのスレッドを再起動させることができます。

条件変数は、待機を準備する 1つのスレッドによって作成される競合状態を回避するために、そして、最初のスレッドが実際に待機する前に状態を通知する別のスレッドがデッドロックを引き起こすことを回避するために、常にミューテックスに関連付けられていなければなりません。
スレッドは、シグナルが送信されないと、永久に待機します。
任意のミューテックスを使えます。ミューテックスと条件変数の間には、明示的なリンクはありません。

CondWait を呼び出すとき、mutex は、既にロックされている必要があります(CondSignalCondBroadCast で使われるものと同じ mutex を使用)。
詳細なシーケンスは、次のとおりです:
- ミューテックスのアトミックロック解除は、このミューテックスを使う他の最終的なスレッドを解放するために、条件変数で待機する前に適用されます(これが、CondWait が、ミューテックスと条件変数の両方を引数としている理由です)。
- スレッドの実行は一時停止され、条件変数が通知されるまで、CPU 時間を消費しません。
- 条件変数が通知されると、ミューテックスは、原子的に再ロックされます。
- スレッドの実行は、CondWait 命令文の後に再開できますが、シグナル呼び出し元が所有する、ロックされたミューテックスのために、中断されます。
- したがって、シグナル呼び出し元は、呼び出されたスレッドが CondWait サブルーチンを完了し、CondWait 呼び出し後の実行が実際に再開できるように、mutex のロックを解除します。

CondSignal は、待機中の 1つのスレッドを再起動します。
CondSignal は、ミューテックスがロックされた後に呼び出す必要があります(CondWait で使われるものと同じミューテックスを使用):
- 条件を待機しているスレッドがない場合、何も起こりません(シグナルは永久に失われます)。
- いくつかが待機している場合、1つだけが再起動されます:
. 複数のスレッドが待機している条件変数が、何度も通知されることがあります。しかし、それを待機しているスレッドの1つが起動されることはありません。
. これは、変数に信号が送られたときに、どの待機スレッドが起動されるかが不明であるためです。
. 目覚めたスレッドは、すぐに条件変数の待機に戻り、変数が再度信号を送られると再び目覚めるなどの場合があります(履歴に基づく、目覚めの優先順位は、保証されません)。
. 悪い振る舞いがあり得る場合、この状況が発生しないことを確認するのは、プログラマー次第です。

CondBroadCast を使う場合、これはすべてのスレッドが一緒に実行されているという意味ではありません:
- 待機関数から戻る前に、それぞれが再びミューテックスをロックしようとします。
- したがって、それらは 1つずつ実行を開始し、それぞれがミューテックスをロックし、作業を行い、次のスレッドが実行する機会を得る前に、ミューテックスを解放します。

注:
- CondWait を、やがて起きる誤ったウェイクアップから保護された方法で使うのは良い習慣です。
- そのため、CondWait は、ループ内に置かれ、スレッドが待機を終了したときに、ブール述語が実際に true であることを確認します。(CondSignalCondBroadCast を実行する直前に、別のスレッドによって、述語が true にされる):
While predicate <> True
Condwait(conditionalid, mutexid)
Wend
predicate = False

- ループは、述部が真の場合にだけ、終了できます。
- 一方、スレッドがループに到達する前に、述語がすでに true の場合、CondWait は完全にスキップされます (そうでなければ失われるはずの CondSignalCondBroadCast のケースを考慮に入れることができます。 なぜなら、最初のスレッドが実際にこれを待っている前に、2番目のスレッドで早まって実行されるためです。)。

上記のすべての適切なルールを適用して、詳細にコーディングする、擬似コード:
  • スレッドの擬似コード・サブ-セクション:
    - "waiting-for" クリティカル・サブ-セクションの擬似コード(他のスレッドの、クリティカル・セクション項目へ/から のリンクを含む):
    '  Principle of mutual exclusion + waiting-for, for a thread sub-section
    '  (connecting lines join the sender(s) and receiver(s) impacted by each action occurring during the sequence)
    '
    '          Thread                                         Other Thread
    '      MUTEXLOCK(mutexID) <-------------------------- from ( atomic_mutex_unlock(mutexID) ) or MUTEXUNLOCK(mutexID)
    '      .......
    '      While booleanT <> True <---------------------- from booleanT = True
    '          ( atomic_mutex_unlock(mutexID) ) --------> to MUTEXLOCK(mutexID) or ( atomic_mutex_re-lock(mutexID) )
    '          CONDWAIT(conditionalID, mutexID) <-------- from CONDSIGNAL(conditionalID)
    '          ( atomic_mutex_re-lock(mutexID) ) <------- from ( atomic_mutex_unlock(mutexID) ) or MUTEXUNLOCK(mutexID)
    '      Wend
    '      booleanT = False
    '      .......
    '      MUTEXUNLOCK(mutexID) ------------------------> to MUTEXLOCK(mutexID) or ( atomic_mutex_re-lock(mutexID) )

    - "signaling" クリティカル・サブ-セクションの擬似コード(他のスレッドのクリティカルセクション項目へ/から のリンクを含む):
    '  Principle of mutual exclusion + signaling, for a thread sub-section
    '  (connecting lines join the sender(s) and receiver(s) impacted by each action occurring during the sequence)
    '
    '          Thread                              Other Thread
    '      MUTEXLOCK(mutexID) <--------------- from ( atomic_mutex_unlock(mutexID) ) or MUTEXUNLOCK(mutexID)
    '      .......
    '      booleanOT = True -----------------> to While booleanOT <> True
    '      CONDSIGNAL(conditionalID) --------> to CONDWAIT(conditionalID, mutexID)
    '      .......
    '      MUTEXUNLOCK(mutexID) -------------> to MUTEXLOCK(mutexID) or ( atomic_mutex_re-lock(mutexID) )

  • スレッドの擬似コード・セクション:
    - "signaling, そして waiting-for" クリティカル・セクションの擬似コード(他のスレッドのクリティカルセクション項目へ/から のリンクを含む):
    '  Principle (1) of mutual exclusion + mutual synchronization, for a thread section
    '  (connecting lines join the sender(s) and receiver(s) impacted by each action occurring during the sequence)
    '
    '          Thread                                        Other Thread
    '      MUTEXLOCK(mutexID) <------------------------- from ( atomic_mutex_unlock(mutexID) ) or MUTEXUNLOCK(mutexID)
    '      Do_something_1_with_exclusion
    '      booleanOT = True ---------------------------> to While booleanOT <> True
    '      CONDSIGNAL(conditionalID) ------------------> to CONDWAIT(conditionalID, mutexID)
    '      While booleanT <> True <--------------------- from booleanT = True
    '          ( atomic_mutex_unlock(mutexID) ) -------> to MUTEXLOCK(mutexID) or ( atomic_mutex_re-lock(mutexID) )
    '          CONDWAIT(conditionalID, mutexID) <------- from CONDSIGNAL(conditionalID)
    '          ( atomic_mutex_re-lock(mutexID) ) <------ from ( atomic_mutex_unlock(mutexID) ) or MUTEXUNLOCK(mutexID)
    '      Wend
    '      booleanT = False
    '      Do_something_2_with_exclusion
    '      MUTEXUNLOCK(mutexID) -----------------------> to MUTEXLOCK(mutexID) or ( atomic_mutex_re-lock(mutexID) )

    - "waiting-for, そして signaling" クリティカル・セクションの擬似コード(他のスレッドのクリティカルセクション項目へ/から のリンクを含む):
    '  Principle (2) of mutual exclusion + mutual synchronization, for a thread section
    '  (connecting lines join the sender(s) and receiver(s) impacted by each action occurring during the sequence)
    '
    '          Thread                                         Other Thread
    '      MUTEXLOCK(mutexID) <-------------------------- from ( atomic_mutex_unlock(mutexID) ) or MUTEXUNLOCK(mutexID)
    '      Do_something_1_with_exclusion
    '      While booleanT <> True <---------------------- from booleanT = True
    '          ( atomic_mutex_unlock(mutexID) ) --------> to MUTEXLOCK(mutexID) or ( atomic_mutex_re-lock(mutexID) )
    '          CONDWAIT(conditionalID, mutexID) <-------- from CONDSIGNAL(conditionalID)
    '          ( atomic_mutex_re-lock(mutexID) ) <------- from ( atomic_mutex_unlock(mutexID) ) or MUTEXUNLOCK(mutexID)
    '      Wend
    '      booleanT = False
    '      Do_something_2_with_exclusion
    '      booleanOT = True ----------------------------> to While booleanOT <> True
    '      CONDSIGNAL(conditionalID) -------------------> to CONDWAIT(conditionalID, mutexID)
    '      MUTEXUNLOCK(mutexID) ------------------------> to MUTEXLOCK(mutexID) or ( atomic_mutex_re-lock(mutexID) )

  • 2つのスレッドの、それぞれの擬似コードセクションの例:
    - メインスレッドの "signaling, そして waiting-for" クリティカル・セクション、および、子スレッドの "waiting-for, そして signaling" クリティカル・セクションの擬似コード(両方のスレッドのクリティカル・セクション項目へ/から のリンクを含む):
    '  Principle (example) of mutual exclusion + mutual synchronization sections, between 2 threads,
    '  using the principle (1) for the main thread, and the principle (2) for the child thread
    '  (connecting lines join the sender(s) and receiver(s) impacted by each action occurring during the sequence)
    '
    '          Main Thread                                                                          Child Thread
    '      MUTEXLOCK(mutexID) <-----------------------------.     .-------------.-------------> MUTEXLOCK(mutexID)
    '      Do_something_1_with_exclusion                    |     |             |               Do_something_1_with_exclusion
    '      boolC = True ----------------------------------- | --- | ----------- | ------------> While boolC <> True
    '      CONDSIGNAL(conditionalID) ---------------------- | --- | ----------- | ---.    .-------- ( atomic_mutex_unlock(mutexID) )
    '      While boolM <> True <--------------------------- | --- | ---------.  |    '--- | ------> CONDWAIT(conditionalID, mutexID)
    '          ( atomic_mutex_unlock(mutexID) ) ------.     |     |          |  '-------- | ------> ( atomic_mutex_re-lock(mutexID) )
    '          CONDWAIT(conditionalID, mutexID) <---- | --- | --- | ------.  |            |     Wend
    '          ( atomic_mutex_re-lock(mutexID) ) <--- | ----'---- | ---.  |  |            |     boolC = False
    '      Wend                                       |           |    |  |  |            |     Do_something_2_with_exclusion
    '      boolM = False                              |           |    |  |  '----------- | --- boolM = True
    '      Do_something_2_with_exclusion              |           |    |  '-------------- | --- CONDSIGNAL(conditionalID)
    '      MUTEXUNLOCK(mutexID) ----------------------'-----------'    '------------------'---- MUTEXUNLOCK(mutexID)

前のページの2番目の例(Mutual Exclusion)は、同期の例を示す条件変数を使って、変更されています。
そのため、以前は相互除外ブロック [Mutexlock ... Mutexunlock] で保護されていた、コードの 2つのセクションは、 各スレッドが、文字シーケンス("[M]" か "(C)")を次々に表示するように、同期されています。

この例では、2つのスレッドのクリティカル・セクションは、逆順のサブ-セクションを持っています:
- メイン・スレッド・クリティカル・セクション:最初に信号を送り、次に待機します。
- 子スレッド・クリティカル・セクション:最初に待機してから、シグナルを送信します。
したがって、メインスレッドは、常に最初にその順番を表示します:
'  Principle of mutual exclusion + mutual synchronization between 2 threads with loop
'  (connecting lines join the sender(s) and receiver(s) impacted by each action occurring during the sequence)
'
'          Main Thread                                                                  Child Thread
'  .....                                                                          .....
'  Loop                                                                           Loop
'      MUTEXLOCK(mutID) <-----------------------------.     .-------------.---------> MUTEXLOCK(mutID)
'      Do_something_with_exclusion              .---- | --- | ----------- | --------> While boolC <> True
'      boolC = True ----------------------------'     |     |             |     .-------- ( atomic_mutex_unlock(mutID) )
'      CONDSIGNAL(condID) --------------------------- | --- | ----------- | --- | ------> CONDWAIT(condID, mutID)
'      While boolM <> True <------------------------- | --- | ---------.  '---- | ------> ( atomic_mutex_re-lock(mutID) )
'          ( atomic_mutex_unlock(mutID) ) ------.     |     |          |        |     Wend
'          CONDWAIT(condID, mutID) <----------- | --- | --- | ------.  |        |     boolC = False
'          ( atomic_mutex_re-lock(mutID) ) <--- | ----'---- | ---.  |  |        |     Do_something_with_exclusion
'      Wend                                     |           |    |  |  '------- | --- boolM = True
'      boolM = False                            |           |    |  '---------- | --- CONDSIGNAL(condID)
'      MUTEXUNLOCK(mutID) ----------------------'-----------'    '--------------'---- MUTEXUNLOCK(mutID)
'  End Loop                                                                       End Loop
'  .....                                                                          .....

Declare Sub thread (ByVal userdata As Any Ptr)

Dim As Any Ptr threadID             '' declaration of an 'Any Ptr' thread-ID of the child thread
Dim Shared As Any Ptr mutID         '' declaration of a global 'Any Ptr' mutex-ID
    mutID = MutexCreate             '' creation of the mutex
Dim Shared As Boolean boolM, boolC  '' declaration of 2 global 'Boolean' boolM and boolC as predicates
Dim Shared As Any Ptr condID        '' declaration of a global 'Any Ptr' conditional-ID
    condID = CondCreate             '' creation of the conditional


Print """[M]"": from 'Main' thread"
Print """(C)"": from 'Child' thread"
Print

threadID = ThreadCreate(@thread)  '' creation of the child thread from the main thread

For I As Integer = 1 To 10       '' 'For' loop of the main thread
    MutexLock(mutID)             '' set mutex locked at the beginning of the exclusive section
    Print "[";
    Sleep 50, 1
    Print "M";
    Sleep 50, 1
    Print "]";
    boolC = True                 '' set to 'True' the predicate for the child thread
    CondSignal(condID)           '' signal to the child thread
    While boolM <> True          '' test predicate from the child thread
        CondWait(condID, mutID)  '' wait for signal from the child thread
    Wend
    boolM = False                '' reset the predicate from the child thread
    MutexUnlock(mutID)           '' set mutex unlocked at the end of the exclusive section
    Sleep 50, 1
Next I

ThreadWait(threadID)  '' waiting for the child thread termination
Print
Print "'Child' thread finished"

MutexDestroy(mutID)  '' destruction of the mutex
CondDestroy(condID)  '' destruction of the conditional

Sleep


Sub thread (ByVal userdata As Any Ptr)  '' sub executed by the child thread
    For I As Integer = 1 To 10          '' 'For' loop of the child thread
        MutexLock(mutID)                '' set mutex locked at the beginning of the exclusive section
        While boolC <> true             '' test predicate from the main thread
            CondWait(condID, mutID)     '' wait for signal from the main thread
        Wend
        boolC = False                   '' reset the predicate from the main thread
        Print "(";
        Sleep 50, 1
        Print "C";
        Sleep 50, 1
        Print ")";
        boolM = True                    '' set to 'True' the predicate for the main thread
        CondSignal(condID)              '' signal to the child thread
        MutexUnlock(mutID)              '' set mutex unlocked at the end of the exclusive section
        Sleep 250, 1
    Next I
End Sub

出力:
"[M]": from 'Main' thread
"(C)": from 'Child' thread

[M](C)[M](C)[M](C)[M](C)[M](C)[M](C)[M](C)[M](C)[M](C)[M](C)
'Child' thread finished

参照
プログラマーのための案内に戻る
←リンク元に戻る プログラム開発関連に戻る

ページ歴史:2021-02-02 12:52:11
日本語翻訳:WATANABE Makoto、原文著作者:fxm

ホームページのトップに戻る

表示-非営利-継承