条件変数 を、作成、待機/シグナル、破棄する、組み込み手続き。
序文:
条件変数は、スレッドが(CPUサイクルを消費せずに)イベント発生まで待機できるメカニズムです。
いくつかのスレッドは、いくつかの他のスレッドがこの条件変数を通知するまで(つまり、通知を送信するまで)、条件変数で待機する場合があります。
この時点で、この条件変数を待機しているスレッドの1つが起動し、イベントに対応できます。
この変数で、ブロードキャスト・メソッドを使うことにより、この条件変数で待機しているすべてのスレッドを、起動することもできます。
条件変数はロックを提供しません。
したがって、この条件変数にアクセスするときに、必要なロックを提供するには、条件変数とともにミューテックスを使う必要があります。
条件付き変数機能(およびミューテックス機能)は、切り離されたスレッドでも、完全に使えます(そのハンドラーだけが識別子によってアクセスできなくなります)。
条件変数の 作成 / 破棄
CondCreate は条件変数を作成し、条件変数を破棄するときに参照される、ハンドル識別子を返します。
CondCreate で作成された条件変数は、不要になったとき、または
CondDestroy でプログラムの終了前に、破棄する必要があります(OS でのリソースのリークを避けるため)。
Create
- 構文:
- 用法:
- 戻り値:
作成された条件変数への、
any ptr ハンドル(条件付き)、または、失敗した場合は NULL ポインター (
0) 。
Destroy
説明
条件変数の待機/シグナル
条件変数のメカニズムにより、スレッドは実行を一時停止し、何らかの条件が満たされるまで、プロセッサを手放します。
CondWait は、何らかの条件が真になるまで、現在のスレッドの実行を停止します。
CondSignal は、条件で待機している 1つのスレッドを再起動できます。一方、
CondBroadCast は、条件で待機しているすべてのスレッドを再起動できます。
Wait-for
- 構文:
- 用法:
- パラメータ:
conditionalid
条件変数のハンドル識別子。
mutexid
この条件変数に関連付けられた、ミューテックスのハンドル識別子。
条件をテストして、
CondWait を呼び出すときに、ロックする必要があります。
Signal
説明
CondCreate で条件変数が作成され、スレッドが開始されると、スレッドのうちの1つ以上(メインプログラムを実行する暗黙のメインスレッドを含む)を、
CondWait によって条件の状態まで待機させることができます。
これらのスレッドは、別のスレッドが
CondSignal によって待機中のスレッドの一つが再起動できることを通知するまで停止します。
CondBroadCast を使うと、条件付きで待っている全てのスレッドを再起動させることができます。
条件変数は、待機を準備する 1つのスレッドによって作成される競合状態を回避するために、そして、最初のスレッドが実際に待機する前に状態を通知する別のスレッドがデッドロックを引き起こすことを回避するために、常にミューテックスに関連付けられていなければなりません。
スレッドは、シグナルが送信されないと、永久に待機します。
任意のミューテックスを使えます。ミューテックスと条件変数の間には、明示的なリンクはありません。
CondWait を呼び出すとき、mutex は、既にロックされている必要があります(
CondSignal か
CondBroadCast で使われるものと同じ mutex を使用)。
詳細なシーケンスは、次のとおりです:
- ミューテックスのアトミックロック解除は、このミューテックスを使う他の最終的なスレッドを解放するために、条件変数で待機する前に適用されます(これが、
CondWait が、ミューテックスと条件変数の両方を引数としている理由です)。
- スレッドの実行は一時停止され、条件変数が通知されるまで、CPU 時間を消費しません。
- 条件変数が通知されると、ミューテックスは、原子的に再ロックされます。
- スレッドの実行は、
CondWait 命令文の後に再開できますが、シグナル呼び出し元が所有する、ロックされたミューテックスのために、中断されます。
- したがって、シグナル呼び出し元は、呼び出されたスレッドが
CondWait サブルーチンを完了し、
CondWait 呼び出し後の実行が実際に再開できるように、mutex のロックを解除します。
CondSignal は、待機中の 1つのスレッドを再起動します。
CondSignal は、ミューテックスがロックされた後に呼び出す必要があります(
CondWait で使われるものと同じミューテックスを使用):
- 条件を待機しているスレッドがない場合、何も起こりません(シグナルは永久に失われます)。
- いくつかが待機している場合、1つだけが再起動されます:
. 複数のスレッドが待機している条件変数が、何度も通知されることがあります。しかし、それを待機しているスレッドの1つが起動されることはありません。
. これは、変数に信号が送られたときに、どの待機スレッドが起動されるかが不明であるためです。
. 目覚めたスレッドは、すぐに条件変数の待機に戻り、変数が再度信号を送られると再び目覚めるなどの場合があります(履歴に基づく、目覚めの優先順位は、保証されません)。
. 悪い振る舞いがあり得る場合、この状況が発生しないことを確認するのは、プログラマー次第です。
CondBroadCast を使う場合、これはすべてのスレッドが一緒に実行されているという意味ではありません:
- 待機関数から戻る前に、それぞれが再びミューテックスをロックしようとします。
- したがって、それらは 1つずつ実行を開始し、それぞれがミューテックスをロックし、作業を行い、次のスレッドが実行する機会を得る前に、ミューテックスを解放します。
注:
- CondWait を、やがて起きる誤ったウェイクアップから保護された方法で使うのは良い習慣です。
- そのため、
CondWait は、ループ内に置かれ、スレッドが待機を終了したときに、ブール述語が実際に true であることを確認します。(
CondSignal か
CondBroadCast を実行する直前に、別のスレッドによって、述語が true にされる):
signal-caller:
predicate = True
Condsignal(conditionalid)
waiting-called:
While predicate <> True
Condwait(conditionalid, mutexid)
Wend
predicate = False
- ループは、述部が真の場合にだけ、終了できます。
- 一方、スレッドがループに到達する前に、述語がすでに true の場合、
CondWait は完全にスキップされます
(そうでなければ失われるはずの
CondSignal や
CondBroadCast のケースを考慮に入れることができます。
なぜなら、最初のスレッドが実際にこれを待っている前に、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
参照
ページ歴史:2023-03-09 09:56:19
日本語翻訳:WATANABE Makoto、原文著作者:fxm