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

FreeBASIC ProPgMtCriticalSectionsFAQ

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

"危険領域(Critical Sections)" に関するよくある質問 左にメニュー・フレームが表示されていない場合は、ここをクリックして下さい

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

マルチスレッドで、"危険領域(Critical Sections)" に関連する質問。

1. いつ、複数のスレッド間で、mutex の 1つの共有変数を、保護することが必須でなくなりますか?
2. 2つのスレッド間で競合する、2つの危険領域(critical section)(ミューテックス・ロックと条件変数シグナリングで)の、コード実行の順序はどうなりますか?
3. ミューテックスをロックしないで 'Condsignal()' や 'Condbroadcast()' を呼び出すと、どうなりますか?
4. ブール述語('Condsignal' か 'Condbroadcast' を動作させる前に、他のスレッドによって設定される)をチェックするために、'While...Wend' ループ内に 'Condwait' を入れることが必須なのはなぜですか?
5. 完全にスレッド-セーフな、ユーザー行入力関数を実装する方法は?
6. マルチ-スレッドで 'Screenlock' を使う方法は?
7. マルチ-スレッドで、「ビデオ・ページング(ダブル・バッファリングかページ・フリッピング)」を使う方法は?
8. マルチ-スレッドで、マルチ-スレッド・アプリケーション(gfxlib2)に、 FB ランタイム・ライブラリを使う方法は?
9. コンソール命令文とキーボード入力を、マルチ-スレッドで使う方法は?
10. スレッドでキーワード 'Sleep' を使う場合、予防策を講じる方がよいですか?
11. マルチ-スレッドを処理するすべてのツールを、ベース・クラスにカプセル化できますか?(ユーザーが独自の実装のために、派生型で拡張する)
12. FreeBASIC で TLS(Thread Local Storage) をエミュレートできますか?
13. FreeBASIC でスレッドプーリング機能をエミュレートできますか?




1. いつ、複数のスレッド間で、mutex の 1つの共有変数を、保護することが必須でなくなりますか?
複数のスレッド間で、共有変数にアクセスする場合、すべてのアクセスは、通常、すべてのスレッドで、ブロック Mutexlock...Mutexunlock 内に配置する必要があります:
- 共有変数が、 size <= sizeof(integer) (アクセス用のアセンブラー命令1つのみ)の、唯一の単純な定義済み数値型の場合、ミューテックスの使用は必須ではありません。
- しかし、これがたとえば、 win32 コンパイルで、共有変数 LongInt の場合、ここでは mutex を使うことを推奨します(そうしないと、スレッドによる読み取りフェーズが、別のスレッドの書き込みフェーズと、交ざる場合があります)。

これは、メモリ内の変数にアクセス(読み取りまたは書き込み)するために、プロセッサが、内部レジスタを使うためです。
N-ビット・プロセッサには、N-ビット・レジスタがありますが、それ以上のものはありません:
- したがって、1つのアセンブラ命令のみで、メモリ内の N ビット変数にアクセスできます。
- 逆に、2N-ビット変数にアクセスするには、2つのアセンブラー命令を使う必要があります。
- これら2つのアセンブラー命令(書き込み用)の間に、別のスレッドがこの同じ変数にアクセス(読み取り用)すると、取得した値は、つじつまが合わなく(互いに、最高 N-ビットと最低 N ビットの不整合)なる可能性があります。

この動作は、2つのスレッドと、mutex なしの共有 LongInt (64-bit) を使う描画プログラムで確認できます:
- 32 ビットでコンパイルすると、多くの読み取り値で、一貫性が失われます。
- 64 ビットでコンパイルすると、一貫性がない読み取り値は、有りません。

下のテスト・プログラムをコンパイルします:
- 32-bit => の場合、多くの誤った点が、円上ではなく、円を含む正方形のどこかにあります。
ミューテックスを起動するため、37/39/58/60 の 4行のコメントを解除すると、取得したすべての点は、円上にのみ、表示されます。
- 64-bit => の場合、ミューテックスが起動していない場合でも、すべての点は、円上だけで有効です。

'   - The "user-defined thread" computes the points coordinates on a circle,
'     and write those in a LongInt (32-bit & 32-bit = 64-bit)
'   - The "main thread" plots the points from the LongInt value.
'
'   Behavior:
'      - The first point must be pre-determined.
'      - Nothing prevents that a same calculated point could be plotted several times
'      (depends on execution times of the loops between main thread and user thread).
'      - Nothing prevents that a calculated point could be not plotted
'      (same remark on the loop times).
'
'   Remark:
'      Voluntarily, there is no Sleep in the loop of each thread (normally strongly discouraged),
'      but this is just in this special case to amplify the behavior effects to observe.


Union Point2D
    Dim As LongInt xy
    Type
        Dim As Long y
        Dim As Long x
    End Type
End Union

Dim As Any Ptr handle
Dim Shared As Any Ptr mutex
Dim Shared As Integer quit

Sub Thread (ByVal param As Any Ptr)
    Const pi As Single = 4 * Atn(1)
    Dim As Point2D Ptr p = param
    Do
        Dim As Point2D P2D0
        Dim As Single teta = 2 * pi * Rnd
        P2D0.x = 320 + 200 * Cos(teta)
        P2D0.y = 240 + 200 * Sin(teta)
'        Mutexlock(mutex)
        p->xy = P2D0.xy
'        Mutexunlock(mutex)
'        Sleep 5, 1
    Loop Until quit = 1
End Sub


Screen 12

Dim As Point2D P2D
P2D.x = 520
P2D.y = 240

mutex = MutexCreate
handle = ThreadCreate(@Thread, @P2D)

Dim As Integer c

Do
    Dim As Point2D P2D0
'    Mutexlock(mutex)
    P2D0.xy = P2D.xy
'    Mutexunlock(mutex)
    PSet (P2D0.x, P2D0.y), c
    c = (c Mod 15) + 1
'    Sleep 5, 1
Loop Until Inkey <> ""
 
quit = 1
ThreadWait(handle)
MutexDestroy(mutex)

ページの頭に戻る



2. 2つのスレッド間で競合する、2つの危険領域(critical section)(ミューテックス・ロックと条件変数シグナリングで)の、コード実行の順序はどうなりますか?
発生する、1つのスレッド・シグナリングの順序:
a) 別のスレッドが待機している間(述語の While ループ内)、
b) 別のスレッドが待機する前(述語の While ループ内)。

#define while_loop_on_predicate

Dim As Any Ptr handle
Dim Shared As Any Ptr mutex
Dim Shared As Any Ptr cond
Dim As Integer sleep0
Dim As Integer sleep1
#ifdef while_loop_on_predicate
Dim Shared As Integer ready
#endif


Sub Thread1 (ByVal param As Any Ptr)
    Sleep *Cast(Integer Ptr, param), 1
    MutexLock(mutex)
    Color 11 : Print "        Thread#1 locks the mutex"
    Color 11 : Print "        Thread#1 executes code with exclusion"
    #ifdef while_loop_on_predicate
    ready = 1
    #endif
    Color 11 : Print "        Thread#1 is signaling"
    CondSignal(cond)
    Color 11 : Print "        Thread#1 executes post-code with exclusion"
    Color 11 : Print "        Thread#1 unlocks the mutex"
    MutexUnlock(mutex)
End Sub

Sub Thread0 (ByVal param As Any Ptr)
    Sleep *Cast(Integer Ptr, param), 1
    MutexLock(mutex)
    Color 10 : Print "    Thread#0 locks the mutex"
    Color 10 : Print "    Thread#0 executes pre-code with exclusion"
    #ifdef while_loop_on_predicate
    While ready <> 1
    #endif
        Color 10 : Print "    Thread#0 is waiting"
        CondWait(cond, mutex)
        Color 10 : Print "    Thread#0 is waked"
    #ifdef while_loop_on_predicate
    Wend
    #endif
    Color 10 : Print "    Thread#0 executes code with exclusion"
    #ifdef while_loop_on_predicate
    ready = 0
    #endif
    Color 10 : Print "    Thread#0 unlocks the mutex"
    MutexUnlock(mutex)
End Sub


mutex = MutexCreate
cond = CondCreate

sleep0 = 0
sleep1 = 1000
Color 7 : Print "Chronology for Thread#1 signaling while Thread#0 is waiting:"
handle = ThreadCreate(@Thread1, @sleep1)
Thread0(@sleep0)
ThreadWait(handle)
Color 7 : Print "Thread#1 finished": Print
Sleep 1000, 1

sleep0 = 1000
sleep1 = 0
Color 7 : Print "Chronology for Thread#1 signaling before Thread#0 is waiting:"
handle = ThreadCreate(@Thread1, @sleep1)
Thread0(@sleep0)
ThreadWait(handle)
Color 7 : Print "Thread#1 finished": Print


MutexDestroy(mutex)
CondDestroy(cond)
Sleep

出力部分 a - スレッド #0 が待機中に、スレッド #1 がシグナルを送る順序:
Chronology for Thread#1 signaling while Thread#0 is waiting:
	Thread#0 locks the mutex
	Thread#0 executes pre-code with exclusion
	Thread#0 is waiting
		Thread#1 locks the mutex
		Thread#1 executes code with exclusion
		Thread#1 is signaling
		Thread#1 executes post-code with exclusion
		Thread#1 unlocks the mutex
	Thread#0 is waked
	Thread#0 executes code with exclusion
	Thread#0 unlocks the mutex
Thread#1 finished

出力部分 b - スレッド #0 が待機する前の、スレッド #1 がシグナルする順序:
Chronology for Thread#1 signaling before Thread#0 is waiting:
		Thread#1 locks the mutex
		Thread#1 executes code with exclusion
		Thread#1 is signaling
		Thread#1 executes post-code with exclusion
		Thread#1 unlocks the mutex
	Thread#0 locks the mutex
	Thread#0 executes pre-code with exclusion
	Thread#0 executes code with exclusion
	Thread#0 unlocks the mutex
Thread#1 finished	

注意:CondWait が述語の While ループ内にない場合(上のプログラムの最初の行にコメントを入れる)、2番目のケース(スレッド #1 は、スレッド #0 が待機する前に信号を送る)で、スレッド #0 は待機フェーズ(Ctrl-C で終了)でブロックされたままであることを、確認できます。

ページの頭に戻る



3. ミューテックスをロックしないで 'Condsignal()' や 'Condbroadcast()' を呼び出すと、どうなりますか?
危険領域(critical section) の例 2 を参照して、次のことを思い出してください:
- スレッドを起動するために、Condsignal()Condbroadcast() を実行している間は、ミューテックスも、常にロックする必要があります(ミューテックスがロック解除されるのは、Condsignal()Condbroadcast() の後だけです)。
- ミューテックスがロックされていない場合(または、Condsignal()Condbroadcast() を実行する直前のみで、ミューテックスがロック解除されている場合でも)、動作は、予測できない可能性があります(スレッドの構成と実行の実時間に応じて、動作したりしなかったり) 。

危険領域(Critical Section) の例2 " すべてのスレッドに対して、condwait を使ってから condbroadcast(および mutex)を使う、同期メソッドの例":
- 少なくとも Mutexunlock() が、Condbroadcast() の直前に移動されると、プログラムは直ちにハングします。
- 一部の人は、ミューテックスは、常に Condsignal()Condbroadcast() の直前に、ロック解除できると認定しますが、Condbroadcast() に対してのみロック解除ができる、と、より慎重に主張するユーザーもいます。しかし、実験では逆のことが示されています!

一般的なルールは、次のとおりです:
- 条件は、スレッドがミューテックスをロックしてから、条件変数 (CondWait()) で待機するまでの間、(Condsignal()Condbroadcast() によって) 通知されてはなりません。
そうしないと、その条件変数のスレッドの待機キューが、破損する可能性があるようです。
- したがって、それを回避して、このルールに従うには、条件が通知されたときに、ミューテックスがロックされたままであることが必要です。

ページの頭に戻る



4. ブール述語('Condsignal' か 'Condbroadcast' を動作させる前に、他のスレッドによって設定される)をチェックするために、'While...Wend' ループ内に 'Condwait' を入れることはなぜ必須ですか?
While predicate <> True
Condwait(conditionalid, mutexid)
Wend
predicate = False


すべてのドキュメントで、こうすることを強く推奨します。主に、最終的な偽のウェイクアップと戦うために、正当化されます。

これはおそらく真実ですが、受信スレッドがまだ CondWait を待機していない間に、信号が早期にアクティブになった場合(信号は永久に失われます)、CondSignal (または CondBroadcast)が失われないようにすることを推奨します:
- この場合、受信スレッドは、CondSignal (又は CondBroadcast)が起動される前に、まだミューテックスをロックしていません。
- したがって、受信スレッドが While ループに到達する前に、述語はすでに true になり、CondWait が完全にスキップされるようになり、最終的なブロック現象を回避します。

2つのスレッド(メイン・プログラムのスレッド #0、ユーザー手続きのスレッド #1、それぞれが、ループでその番号を出力します)が、ほぼ同じ実行時間を持ち、それぞれが、その番号を織り合わせるために互いに同期します( 1つの mutex と、2つの条件変数と、CondSignal/CondWait を使って):
  • 述語の While ループがない場合、プログラムは直ぐにハングします(Ctrl-C で終了):
    '            Thread#0                           XOR + <==>                       Thread#1
    '   .....                                                             .....
    '   MutexLock(mut) <-------------------------.             .----------> MutexLock(mut)
    '   ( atomic_mutex_unlock(mut) ) ------.     |             |            Do_something_with_exclusion
    '   CondWait(cond#1, mut) <----------- | --- | ----------- | ---------- CondSignal(cond#1)
    '   ( atomic_mutex_re-lock(mut) ) <--- | ----'----.        |     .----- ( atomic_mutex_unlock(mut) )
    '   Do_something_with_exclusion        |     .--- | ------ | --- | ---> CondWait(cond#2, mut)
    '   CondSignal(cond#2) --------------- | ----'    |    .---'---- | ---> ( atomic_mutex_re-lock(mut) )
    '   Do_something_with_exclusion        |     .--- | ---'         |      Do_something_with_exclusion
    '   MutexUnlock(mut) ------------------'-----'    '--------------'----- MutexUnlock(mut)
    '   .....                                                               .....
    '
    '  (connecting lines join the sender(s) and receiver(s) impacted by each action occurring during the sequence)

    Dim As Any Ptr handle
    Dim Shared As Any Ptr mutex
    Dim Shared As Any Ptr cond1
    Dim Shared As Any Ptr cond2
    Dim Shared As Integer quit

    Sub Thread (ByVal param As Any Ptr)
        Do
            MutexLock(mutex)
            Print "1";
            CondSignal(cond1)
            CondWait(cond2, mutex)
            If quit = 1 Then
                MutexUnlock(mutex)
                Exit Do
            End If
            MutexUnlock(mutex)
            Sleep 1, 1
        Loop
    End Sub


    mutex = MutexCreate
    cond1 = CondCreate
    cond2 = CondCreate
    handle = ThreadCreate(@Thread)

    Do
        MutexLock(mutex)
        CondWait(cond1, mutex)
        Print "0";
        CondSignal(cond2)
        If Inkey <> "" Then
            quit = 1
            MutexUnlock(mutex)
            Exit Do
        End If
        MutexUnlock(mutex)
        Sleep 1, 1
    Loop
     
    ThreadWait(handle)
    MutexDestroy(mutex)
    CondDestroy(cond1)
    CondDestroy(cond2)
    Print

    Sleep

  • CondWait の周りの述語に 'While...Wend' ループを使うと、ブロッキング現象は発生しません:
    '            Thread#0                                 XOR + <==>                            Thread#1
    '   .....                                                                          .....
    '   MutexLock(mut) <-----------------------------.                          .----> MutexLock(mut)
    '   While bool#1 <> true <---------------------- | --------.                |      Do_something_with_exclusion
    '       ( atomic_mutex_unlock(mut) ) ------.     |         '--------------- | ---- bool#1 = true
    '       CondWait(cond#1, mut) <----------- | --- | ------------------------ | ---- CondSignal(cond#1)
    '       ( atomic_mutex_re-lock(mut) ) <--- | ----'----.    .--------------- | ---> While bool#2 <> true
    '   Wend                                   |          |    |          .---- | -------- ( atomic_mutex_unlock(mut) )
    '   bool#1 = false                .------- | -------- | ---'  .------ | --- |--------> CondWait(cond#2, mut)
    '   Do_something_with_exclusion   |   .--- | -------- | ------'  .--- | ----'--------> ( atomic_mutex_re-lock(mut) )
    '   bool#2 = true ----------------'   |    |     .--- | ---------'    |            Wend
    '   CondSignal(cond#2) ---------------'    |     |    |               |            bool#2 = false
    '   Do_something_with_exclusion            |     |    |               |            Do_something_with_exclusion
    '   MutexUnlock(mut) ----------------------'-----'    '---------------'----------- MutexUnlock(mut)
    '   .....                                                                          .....
    '
    '  (connecting lines join the sender(s) and receiver(s) impacted by each action occurring during the sequence)

    Dim As Any Ptr handle
    Dim Shared As Any Ptr mutex
    Dim Shared As Any Ptr cond1
    Dim Shared As Any Ptr cond2
    Dim Shared As Integer new1
    Dim Shared As Integer new2
    Dim Shared As Integer quit

    Sub Thread (ByVal param As Any Ptr)
        Do
            MutexLock(mutex)
            Print "1";
            new1 = 1
            CondSignal(cond1)
            While new2 <> 1
                CondWait(cond2, mutex)
            Wend
            new2 = 0
            If quit = 1 Then
                MutexUnlock(mutex)
                Exit Do
            End If
            MutexUnlock(mutex)
            Sleep 1, 1
        Loop
    End Sub


    mutex = MutexCreate
    cond1 = CondCreate
    cond2 = CondCreate
    handle = ThreadCreate(@Thread)

    Do
        MutexLock(mutex)
        While new1 <> 1
            CondWait(cond1, mutex)
        Wend
        new1 = 0
        Print "0";
        new2 = 1
        CondSignal(cond2)
        If Inkey <> "" Then
            quit = 1
            MutexUnlock(mutex)
            Exit Do
        End If
        MutexUnlock(mutex)
        Sleep 1, 1
    Loop
     
    ThreadWait(handle)
    MutexDestroy(mutex)
    CondDestroy(cond1)
    CondDestroy(cond2)
    Print

    Sleep

4.1. 'Condwait' (および 'Condsignal' や 'Condbroadcast')は、既に、他のスレッドによって設定されたブール述語をチェックする 'While...Wend' ループが存在しても、まだ有益ですか?
(前の質問によって引き起こされた別の質問)
- 推奨される構造は次のとおりです:
'  Principle of mutual exclusion + CONDWAIT in a While...Wend loop with predicate check, 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) )

'  Principle of mutual exclusion + CONDSIGNAL with predicate check, 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) )

- 'CondWait' を使わない場合、代わりに、ブール・フラグの 'While...Wend' ループに 'Sleep x, 1' 命令を配置して、ループ時にタイム・スライスを解放する必要があります。
(さらに、 この 'Sleep x, 1' は、別のスレッドを解放するために ['Mutexunlock'...'Mutexlock'] ブロック内に配置する必要があります):
'  Principle of mutual exclusion + SLEEP in a While...Wend loop with predicate check, 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 MUTEXUNLOCK(mutexID)
'      .......
'      While booleanT <> True <--------------------- from booleanT = True
'          MUTEXUNLOCK(mutexID) -------------------> to MUTEXLOCK(mutexID)
'          SLEEP(tempo, 1)
'          MUTEXLOCK(mutexID) <--------------------- from MUTEXUNLOCK(mutexID)
'      Wend
'      booleanT = False
'      .......
'      MUTEXUNLOCK(mutexID) -----------------------> to MUTEXLOCK(mutexID)

'  Principle of mutual exclusion + predicate check only, 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 MUTEXUNLOCK(mutexID)
'      .......
'      booleanOT = True ---------------------------> to While booleanOT <> True
'      .......
'      MUTEXUNLOCK(mutexID) -----------------------> to MUTEXLOCK(mutexID)

'CondWait' の間、スレッドの実行は中断され、条件変数が通知されるまで、CPU 時間を消費しません。
ただし、代わりに 'Sleep x, 1' が設定されている場合、待機時間は事前に決定されていて、'CondWait' のような自己適応ではありません。

=> 'CondWait' は、実行時間を最適化するのに役立ちます。

ページの頭に戻る



5. ユーザー行入力関数を完全にスレッド-セーフで実装する方法は?
別のスレッドも入出力リソースにアクセスする必要がある場合、Input キーワードは、スレッド-セーフではない場合があります:
- Input 命令文を実行するとき、他の実行中のスレッドは、テキスト・カーソルの位置を変更してはなりません。これにより、LocatePrint、...などの命令文が禁止されます。
- さらに、Input キーワードを、ミューテックス・ロックで囲むことはできません(Inkey キーワードでできるように)。入力行は完了せず検証されますが、入力/出力にアクセスしたい他のスレッドは完全にブロックされるためです。 (ミューテックスのロック解除を待っています)。

スレッド-セーフな入力行関数(入力/出力リソースに対して):
入力位置、プロンプト・メッセージ、スリープ時間、ライン-ブランキング・コマンド、mutex ポインターは、Inkey キーワードの周りをまわることで、単純化された入力関数をシミュレートする、スレッドセーフな、次の threadInput() 関数に渡すことができます。(すべての入出力キーワードは、相互排他ロック・ブロックで囲まれている必要があり、カーソル位置は、相互排他ロック・ブロックの終了ごとに、復元する必要があります):

Function threadInput (ByVal row As Integer, ByVal column As Integer, ByRef prompt As String = "", _
                      ByVal sleeptime As Integer = 15, ByVal blank As Integer = 0, ByVal mutex As Any Ptr = 0 _
                      ) As String
    Dim As String inputchr
    Dim As String inputline
    Dim As Integer cursor
    Dim As Integer cursor0
    Dim As Integer r
    Dim As Integer c

 
    MutexLock(mutex)
    r = CsrLin()
    c = Pos()
    Locate row, column
    Print prompt & " _";
    cursor0 = Pos() - 1
    Locate r, c
    MutexUnlock(mutex)

    Do
        MutexLock(mutex)
        r = CsrLin()
        c = Pos()
        inputchr = Inkey
        If inputchr <> "" Then
            If inputchr >= Chr(32) And inputchr < Chr(255) Then
                inputline = Left(inputline, cursor) & inputchr & Mid(inputline, cursor + 1)
                cursor += 1
            ElseIf inputchr = Chr(08) And Cursor > 0 Then                         'BkSp
                cursor -= 1
                inputline = Left(inputline, cursor) & Mid(inputline, cursor + 2)
            ElseIf inputchr = Chr(255) & "S" And Cursor < Len(inputline) Then     'Del
                inputline = Left(inputline, cursor) & Mid(inputline, cursor + 2)
            ElseIf inputchr = Chr(255) + "M" And Cursor < Len(inputline) Then     'Right
                Cursor += 1
            ElseIf inputchr = Chr(255) + "K" And Cursor > 0 Then                  'Left
                Cursor -= 1
            End If
            If inputchr = Chr(27) Then                                            'Esc
                Locate row, cursor0
                Print Space(Len(inputline) + 1);
                inputline = ""
                cursor = 0
            End If
            Locate row, cursor0
            Print Left(inputline, cursor) & Chr(95) & Mid(inputline, cursor + 1) & " ";
        End If
        Locate r, c
        MutexUnlock(mutex)
        Sleep sleeptime, 1
    Loop Until inputchr = Chr(13)

    If blank <> 0 Then
        MutexLock(mutex)
        r = CsrLin()
        c = Pos()
        Locate row, cursor0
        Print Space(Len(inputline) + 1);
        Locate r, c
        MutexUnlock(mutex)
    End If

    Return inputline
End Function

  • Critical Sections ページの「すべてのスレッドにミューテックスを使用した非同期メソッドの例」の、例1から、実行中のマルチ・スレッド・コードは、プログラムを終了するために "quit" コマンドを待っています:

    '   User thread algorithm:
    '
    '     Do
    '     |  Mutexlock
    '     |  .....
    '     |  Critical section of code
    '     |  .....
    '     |  Mutexunlock
    '     |  Sleep my_tempo, 1
    '     Loop Until quit = true
    '
    '   There is no any advantage or disadvantage between threads for running their critical sections.

    Function threadInput (ByVal row As Integer, ByVal column As Integer, ByRef prompt As String = "", _
                          ByVal sleeptime As Integer = 15, ByVal blank As Integer = 0, ByVal mutex As Any Ptr = 0 _
                          ) As String
        Dim As String inputchr
        Dim As String inputline
        Dim As Integer cursor
        Dim As Integer cursor0
        Dim As Integer r
        Dim As Integer c

     
        MutexLock(mutex)
        r = CsrLin()
        c = Pos()
        Locate row, column
        Print prompt & " _";
        cursor0 = Pos() - 1
        Locate r, c
        MutexUnlock(mutex)

        Do
            MutexLock(mutex)
            r = CsrLin()
            c = Pos()
            inputchr = Inkey
            If inputchr <> "" Then
                If inputchr >= Chr(32) And inputchr < Chr(255) Then
                    inputline = Left(inputline, cursor) & inputchr & Mid(inputline, cursor + 1)
                    cursor += 1
                ElseIf inputchr = Chr(08) And Cursor > 0 Then                         'BkSp
                    cursor -= 1
                    inputline = Left(inputline, cursor) & Mid(inputline, cursor + 2)
                ElseIf inputchr = Chr(255) & "S" And Cursor < Len(inputline) Then     'Del
                    inputline = Left(inputline, cursor) & Mid(inputline, cursor + 2)
                ElseIf inputchr = Chr(255) + "M" And Cursor < Len(inputline) Then     'Right
                    Cursor += 1
                ElseIf inputchr = Chr(255) + "K" And Cursor > 0 Then                  'Left
                    Cursor -= 1
                End If
                If inputchr = Chr(27) Then                                            'Esc
                    Locate row, cursor0
                    Print Space(Len(inputline) + 1);
                    inputline = ""
                    cursor = 0
                End If
                Locate row, cursor0
                Print Left(inputline, cursor) & Chr(95) & Mid(inputline, cursor + 1) & " ";
            End If
            Locate r, c
            MutexUnlock(mutex)
            Sleep sleeptime, 1
        Loop Until inputchr = Chr(13)

        If blank <> 0 Then
            MutexLock(mutex)
            r = CsrLin()
            c = Pos()
            Locate row, cursor0
            Print Space(Len(inputline) + 1);
            Locate r, c
            MutexUnlock(mutex)
        End If

        Return inputline
    End Function

    '------------------------------------------------------------------------------

    Type UDT
        Dim As Integer number
        Dim As Integer tempo
        Dim As Any Ptr pThread
        Dim As ULongInt count
        Static As Any Ptr pMutex
        Static As Integer numberMax
        Static As Integer quit
    End Type
    Dim As Any Ptr UDT.pMutex
    Dim As Integer UDT.numberMax
    Dim As Integer UDT.quit

    Sub Counter (ByVal pt As UDT Ptr)
        With *pt
            Locate .number, .number, 0
            Sleep 5, 1
            .count += 1
            Print .count;
        End With
    End Sub

    Sub Thread (ByVal p As Any Ptr)
        Dim As Integer myquit
        Dim As UDT Ptr pUDT = p
        With *pUDT
            Do
                MutexLock(.pMutex)
                Counter(pUDT)
                myquit = .quit
                MutexUnlock(.pMutex)
                Sleep .tempo, 1
            Loop Until myquit = 1
        End With
    End Sub


    Screen 12
    UDT.numberMax = 6

    Dim As UDT u(0 To UDT.numberMax)
    For I As Integer = 0 To UDT.numberMax
        u(I).number = i
        u(I).tempo = 100 + 15 * I - 95 * Sgn(I)
    Next I
    UDT.pMutex = MutexCreate

    Dim As Single t = Timer
    For I As Integer = 1 To UDT.numberMax
        u(I).pThread = ThreadCreate(@Thread, @u(I))
    Next I

    Do
    Loop Until LCase(threadInput(8, 1, """quit"" for exit?", 10, 1, UDT.pMutex)) = "quit"

    UDT.quit = 1

    For I As Integer = 1 To UDT.numberMax
        ThreadWait(u(I).pThread)
    Next I
    t = Timer - t

    MutexDestroy(UDT.pMutex)
    Dim As ULongInt c
    For I As Integer = 1 To UDT.numberMax
        c += u(I).count
    Next I
    Locate UDT.numberMax + 4, 1
    Print CULngInt(c / t) & " increments per second"

    Sleep

注意:
それ以外では、スレッドで、Line, Draw String, Put として、描画キーワードだけを使って(描画カーソルの位置だけを使う)、mutex なしでメインコードの Line Input キーワードと互換性のあるスレッド-セーフ手続きを誘導します:

Type UDT
    Dim As Integer number
    Dim As Integer tempo
    Dim As Any Ptr pThread
    Dim As ULongInt count
    Dim As Any Ptr img
    Static As Integer numberMax
    Static As Integer quit
End Type
Dim As Integer UDT.numberMax
Dim As Integer UDT.quit

Const As String prompt = "Enter ""quit"" for exit"
Dim As String s

Sub Counter (ByVal pt As UDT Ptr)  ' for a graphic character size 8x16
    With *pt
        Line .img, (0, 0)-(20 * 8 - 1, 16 - 1), 0, BF            ' clearing the image buffer
        Sleep 5, 1
        .count += 1
        Draw String .img, (0, 0), Str(.count)                    ' drawing in the image buffer
        Put ((.number - 1) * 8, (.number - 1) * 16), .img, PSet  ' copying the image buffer to screen
    End With
End Sub

Sub Thread (ByVal p As Any Ptr)    ' for a graphic character size 8x16
    Dim As UDT Ptr pUDT = p
    With *pUDT
        .img = ImageCreate(20 * 8, 16)  ' using an image buffer to avoid flickering
        Do
            Counter(pUDT)
            Sleep .tempo, 1
        Loop Until .quit = 1
        ImageDestroy .img  ' destroying the image buffer
    End With
End Sub


Screen 12
UDT.numberMax = 6

Dim As UDT u(0 To UDT.numberMax)
For I As Integer = 0 To UDT.numberMax
    u(I).number = i
    u(I).tempo = 100 + 15 * I - 95 * Sgn(I)
Next I

Dim As Single t = Timer
For I As Integer = 1 To UDT.numberMax
    u(I).pThread = ThreadCreate(@Thread, @u(I))
Next I

Do
    Locate 8, 1, 0
    Line Input; prompt; s
    Locate , Len(prompt) + 3
    Print Space(Len(s));
Loop Until LCase(s) = "quit"
UDT.quit = 1

For I As Integer = 1 To UDT.numberMax
    ThreadWait(u(I).pThread)
Next I
t = Timer - t

Dim As ULongInt c
For I As Integer = 1 To UDT.numberMax
    c += u(I).count
Next I
Locate UDT.numberMax + 4, 1
Print CULngInt(c / t) & " increments per second"

Sleep

ページの頭に戻る



6. マルチ-スレッドで 'Screenlock' を使う方法は?
- Screenlock...Scrennunlock ブロックは、マルチスレッドと互換性がありません(そうでなければ、プログラムがハングします)。
これが、ミューテックス・ブロックが、相互排除を確実にするために、このような各ブロックの周りに、使われる必要がある理由です。
- 入力キーワード(キーボード、マウスなど)は、画面がロックされていると、安全に実行できないため、このようなキーワードは Screenlock...Screenunlock ブロックの外側にある必要があり、そして、独自のスレッドの Screenlock...Screenunlock ブロックの外側にある必要があります。また、mutex ブロックによって、他のスレッドの、すべての Screenlock...Screenunlock ブロックを保護します。
したがって、キー入力や行入力を待機する GetkeyInput 命令文は使えませんが、待機しない Inkey は機能します。

いくつかのルールを綿密に適用することにより、スレッド内で Screenlock/Screenunlock を使用できます。
メインコード(メインスレッド)を含む、すべてのスレッドの、コーディングの原則:
Do
	' instructions without display (printing/drawing, ...) neither input (input/inkey/mouse getting, ...)
	MutexLock(m)
	Screenlock
	' instructions with only display (printing/drawing, ...)
	Screenunlock
	' instructions with only input without waiting (inkey/mouse getting, ...)
	MutexUnlock(m)
	Sleep tempo, 1
Loop Until condition

ページの頭に戻る



7. マルチ-スレッドで、「ビデオ・ページング(ダブル・バッファリングかページ・フリッピング)」を使う方法は?
「画面ロック」(上の段落を参照)の代わりに、「ビデオ・ページング(ダブル・バッファリングまたはページ・フリッピング)」をマルチスレッドでより簡単に使用できます。ただ、gfxlib2 の多くの状態が、Screenset ( また、 View 設定、描画カーソル位置、描画カラーなど)のように、スレッドに依存しているこに注意して下さい。
このため、マルチ・ビデオページ構成で動作する各スレッド・コードで、作業ページと表示ページの設定を常に制御する必要があります。

注意: これらの 2つの例では、相互排他ミューテックス・コード・ブロックが、2つのスレッドで必須です。コンソール命令文 + Inkey を使うためだけでなく、ダブル・バッファリング・メソッドを使う、描画命令文 + Screencopy だけを使うためです。
(ちらつき防止プロセスがない場合、描画命令文は、排他ミューテックス・コード・ブロックの外側にあることができます。)

ページの頭に戻る



8. マルチ-スレッドで、マルチ-スレッド・アプリケーション(gfxlib2)に、 FB ランタイム・ライブラリを使うには?
gfxlib2 のソースコードは、TLS(Thread Local Storage)を使って多くの状態を保存するため、多くのことはスレッド固有です。
gfxlib2 はスレッドセーフであるため、描画命令文自体(Draw String を含む)で、スレッド間のミューテックスを除外する必要はありません。
対照的に、Locate, Print, ...などのコンソール命令文は、前述のようにスレッドセーフではありません(たとえば、テキスト・カーソルの位置は、すべてのスレッドに共通です)。

ページの頭に戻る



9. コンソール命令文とキーボード入力を、マルチ-スレッドで使うには?
コンソール命令文(Locate, Print, Color, ...など)、および Graphics ウィンドウの LocatePrint(Graphics ウィンドウの Color は除く)、およびキーボード入力(Inkey, Getkey, Input, ...など)は、スレッド-セーフではありません:
- したがって、異なるスレッドの競合セクションで使用される場合、相互排他は、ブロックの最後(自身の使用の後)で、以前(ブロックの始まり)と同様に、コードが状態(テキストカーソルの位置、コンソールの色など)を復元できる、ミューテックス・ロック・ブロックによって必須です。
- ただし、GetkeyInput キーワードは、ミューテックス・ロック・ブロック内に含めることはできません(Inkey キーワードは実行できます)。なぜなら、キーボード入力が完了しない限り、他の競合するスレッドは、完全にブロックされる(ミューテックスのロック解除を待っています)からです。

  • LocatePrint キーワードが、コンソール・ウィンドウまたは描画ウィンドウに適用された場合に、スレッドセーフではないことを示す例(テキスト・カーソル は、2つのケースでスレッドに依存していないことを示します):

    Sub Thread (ByVal p As Any Ptr)
        Locate Cast(Integer, p), Cast(Integer, p)
        For I As Integer = 1 To 50 - 2 * Cast(Integer, p)
            Sleep 20 * Cast(Integer, p), 1
            Print Str(Cast(Integer, p));
        Next I
    End Sub

    Sub test ()
        Dim As Any Ptr p(1 To 9)
        For I As Integer = 1 To 9
            p(I) = ThreadCreate(@Thread, Cast(Any Ptr, I))
            Sleep 25, 1
        Next I
        For I As Integer = 1 To 9
            ThreadWait(p(I))
        Next I
    End Sub

    Screen 0
    test()
    Locate 15, 1
    Print "Any key to continue"
    Sleep

    Screen 12
    test()
    Locate 15, 1
    Print "Any key to quit"
    Sleep

    注意: 各スレッドは、コンソール・ウィンドウと描画ウィンドウの、スレッド番号(1〜9 の ID)に対応する独自の行に、書き込みしないことがわかります。

  • 上の例から、スレッド・コードは、ミューテックス・ロック・ブロックおよび、カーソルの移動の前/後にカーソルの状態を保存/復元することにより、競合するセクションで、完了しています:

    Dim Shared As Any Ptr mutex

    Sub Thread (ByVal p As Any Ptr)
        MutexLock(mutex)
        Dim As Long l0 = Locate()
        Locate Cast(Integer, p), Cast(Integer, p)
        Dim As Long l = Locate()
        Locate HiByte(LoWord(l0)), LoByte(LoWord(l0)), HiWord(l0)
        MutexUnlock(mutex)
        For I As Integer = 1 To 50 - 2 * Cast(Integer, p)
            Sleep 20 * Cast(Integer, p), 1
            MutexLock(mutex)
            l0 = Locate()
            Locate HiByte(LoWord(l)), LoByte(LoWord(l)), HiWord(l)
            Print Str(Cast(Integer, p));
            l = Locate()
            Locate HiByte(LoWord(l0)), LoByte(LoWord(l0)), HiWord(l0)
            MutexUnlock(mutex)
        Next I
    End Sub

    Sub test ()
        Dim As Any Ptr p(1 To 9)
        For I As Integer = 1 To 9
            p(I) = ThreadCreate(@Thread, Cast(Any Ptr, I))
            Sleep 25, 1
        Next I
        For I As Integer = 1 To 9
            ThreadWait(p(I))
        Next I
    End Sub

    mutex = MutexCreate

    Screen 0
    test()
    Locate 15, 1
    Print "Any key to continue"
    Sleep

    Screen 12
    test()
    Locate 15, 1
    Print "Any key to quit"
    Sleep

    MutexDestroy(mutex)

    注意: 各スレッドが、コンソール・ウィンドウと、描画ウィンドウの、スレッド番号(1〜9の ID)に対応する自身の行に書き込みを行っていることがわかります。

  • Color キーワードが、コンソール・ウィンドウに適用されるとスレッドセーフではないが、描画ウィンドウに適用されるとスレッドセーフであることを示す例 (この場合、色はスレッドに依存しています):

    Sub Thread (ByVal p As Any Ptr)
        Color Cast(Integer, p) + 8, Cast(Integer, p)
        For I As Integer = 1 To 50 - 2 * Cast(Integer, p)
            Print " " & Cast(Integer, p) & " ";
            Sleep 20 * Cast(Integer, p), 1
        Next I
    End Sub

    Sub test ()
        Dim As Any Ptr p(1 To 9)
        Locate 1, 1
        For I As Integer = 1 To 9
            p(I) = ThreadCreate(@Thread, Cast(Any Ptr, I))
            Sleep 25, 1
        Next I
        For I As Integer = 1 To 9
            ThreadWait(p(I))
        Next I
        Locate 16, 1
    End Sub

    Screen 0
    test()
    Print "Any key to continue"
    Sleep

    Screen 12
    test()
    Print "Any key to quit"
    Sleep

    注意: 前景/背景色は、コンソール・ウィンドウの、スレッド番号(1〜9 の ID)に固有ではないことがわかります。しかし、これは描画ウィンドウではうまく機能します。

  • 上の例から、スレッド・コードは、ブロックをミューテックス・ロックし、独自のカラー値の使用の前/後に、カラー状態を保存/復元することにより、競合するセクションで完了しています:

    Dim Shared As Any Ptr mutex

    Sub Thread (ByVal p As Any Ptr)
        MutexLock(mutex)
        Dim As Ulong c0 = Color(Cast(Integer, p) + 8, Cast(Integer, p))
        Dim As Ulong c = Color()
        Color(LoWord(c0), HiWord(c0))
        MutexUnlock(mutex)
        For I As Integer = 1 To 50 - 2 * Cast(Integer, p)
            MutexLock(mutex)
            c0 = Color(LoWord(c), HiWord(c))
            Print " " & Cast(Integer, p) & " ";
            Color(LoWord(c0), HiWord(c0))
            MutexUnlock(mutex)
            Sleep 20 * Cast(Integer, p), 1
        Next I
    End Sub

    Sub test ()
        Dim As Any Ptr p(1 To 9)
        Locate 1, 1
        For I As Integer = 1 To 9
            p(I) = ThreadCreate(@Thread, Cast(Any Ptr, I))
            Sleep 25, 1
        Next I
        For I As Integer = 1 To 9
            ThreadWait(p(I))
        Next I
        Locate 16, 1
    End Sub

    mutex = MutexCreate

    Screen 0
    test()
    Print "Any key to continue"
    Sleep

    Screen 12
    test()
    Print "Any key to quit"
    Sleep

    MutexDestroy(mutex)

    注意: 前景/背景色は、コンソール・ウィンドウの、スレッド番号(1〜9 の ID)に、固有であることがわかります(明らかに、これは描画ウィンドウでは常に機能します)。

したがって、スレッドの競合セクションで、Getkey や Input を使うと:
- 単一スレッド(メイン・スレッドなど)だけが、競合するセクションで、コンソール命令文(Locate, Print, Color, ...など)と Inkey に加えて、GetkeyInput を使えます。
- 他のスレッドは、競合するセクションで、コンソール命令文やキーボード入力キーワードを使ってはなりません。しかし、コンソール描画命令文(Pset, Line, Circle, Draw String, graphic Color, ...など)は、それ自体がスレッドセーフなので使えます。(問題なく、メインスレッドでグラフィカルに走査できます。)
- InputGetkey は、スレッドの競合セクションでの画面ロックの使用も、除外します(ちらつき防止方法として、ダブル・バッファリングを推奨します)。

  • スレッド(ここではユーザースレッド)の描画命令文(LineDraw StringScreencopy など)は、除外を使わずに、別のスレッド(ここではメインスレッド)の、コンソール命令文(LocatePrintInput など)と、競合できることを示す例 (ミューテックスによる):

    #include "vbcompat.bi"

    Screen 12, , 2
    ScreenSet 1, 0  
    Color 0, 7
    Cls

    Dim Shared terminate As Integer = 0

    Sub thread (ByVal param As Any Ptr)  
        ScreenSet 1, 0
        Do
            Line (16, 432)-Step(96, 32), 11, BF  'clear the print area
            Sleep 100, 1
            Draw String (24, 432), Format(Now,"dd/mm/yyyy"), 0
            Draw String (32, 448), Format(Now,"hh:mm:ss"), 0
            ScreenCopy
            Sleep 100, 1
        Loop Until terminate = 1
    End Sub

    Dim As String reply
    Locate 2, 2
    Print "Enter ""quit"" to quit"
    ScreenCopy

    Dim p As Any Ptr = ThreadCreate(@thread)

    Do
        Locate 3, 2
        Print Space(Len(reply) + 2);
        Locate 3, 2
        Input reply
    Loop Until LCase(reply) = "quit"

    Print " Stop the thread"
    ScreenCopy
    terminate=1
    ThreadWait (p)
    Print " Thread terminated"
    ScreenCopy

    Sleep

    注意: 'clear the print area' 行の直後の Sleep x, 1 キーワードは、ダブル・バッファリングが使われていない場合に、ちらつきを強調するためだけにここにあります(Input の使用により画面ロックが禁止されています)。

  • 上の例から、表示する日付と表示する時間が、2つの別個のユーザー・スレッドになった場合、これらの 2つのスレッド間の、排他ミューテックス・コード・ブロックだけが必須です。描画命令文自体が競合するからではなく、これら2つのユーザー・スレッドだけを競合させる、ダブル・バッファリング・メソッドが使われている(ちらつきに対する)からです:

    #include "vbcompat.bi"

    Screen 12, , 2
    ScreenSet 1, 0  
    Color 0, 7
    Cls

    Dim Shared terminate As Integer = 0
    Dim Shared mutex As Any Ptr

    Sub thread1 (ByVal param As Any Ptr)  
        ScreenSet 1, 0
        Do
            MutexLock(mutex)
            Line (16, 432)-Step(96, 16), 11, BF  'clear the print area
            Sleep 200, 1
            Draw String (24, 432), Format(Now,"dd/mm/yyyy"), 0
            ScreenCopy
            MutexUnlock(mutex)
            Sleep 100, 1
        Loop Until terminate = 1
    End Sub

    Sub thread2 (ByVal param As Any Ptr)  
        ScreenSet 1, 0
        Do
            MutexLock(mutex)
            Line (16, 448)-Step(96, 16), 11, BF  'clear the print area
            Sleep 100, 1
            Draw String (32, 448), Format(Now,"hh:mm:ss"), 0
            ScreenCopy
            MutexUnlock(mutex)
            Sleep 100, 1
        Loop Until terminate = 1
    End Sub

    Dim As String reply
    Locate 2, 2
    Print "Enter ""quit"" to quit"
    ScreenCopy

    mutex = MutexCreate
    Dim p1 As Any Ptr = ThreadCreate(@thread1)
    Dim p2 As Any Ptr = ThreadCreate(@thread2)

    Do
        Locate 3, 2
        Print Space(Len(reply) + 2);
        Locate 3, 2
        Input reply
    Loop Until LCase(reply) = "quit"

    Print " Stop the threads"
    ScreenCopy
    terminate=1
    ThreadWait (p1)
    ThreadWait (p2)
    MutexDestroy(mutex)
    Print " Threads terminated"
    ScreenCopy

    Sleep

    注意: 'clear the print area' 行の直後の Sleep x, 1 キーワードは、ダブル・バッファリングが使われていない場合の、ちらつきを強調するためだけに、ここにあります(Input の使用により、画面ロックが禁止されています)。

ページの頭に戻る



10. スレッドでキーワード 'Sleep' を使う場合、予防策を講じる方がよいですか?
マルチスレッド文脈で、キーワード Sleep の完全な動作については、まだ疑問が残っています。

したがって、これを使うときには、次の予防措置を講じることを推奨します:
- スレッドの危険領域(Critical Sections) でどうしても必要な場合は、構文 Sleep xSleep x, 0 は、キープレスの内部テストを誘発するため、安全性を最大にするために、他のスレッドとの同時競合を可能な限り回避するため、Inkey キーワードと同じように扱う必要があります。
- それ以外は、相互排除による保護がない場合、構文 Sleep x, 1 (キー押下の内部テストを行わない)が推奨されます。
相互排除は、他のスレッドのタイム・スライスを解放する場合があります。

ページの頭に戻る



11. マルチ-スレッドを処理するすべてのツールを、ベース・クラスにカプセル化できますか?(ユーザーが独自の実装のために、派生型で拡張する)
単純な 'threadUDT' ベース・クラスは、次のように定義できます:
- 各ハンドルに、プライベート 'Any Ptr' 非静的メンバー・フィールドを使って、
- 1つの共有ミューテックスの、プライベート 'Any Ptr' 静的メンバー・フィールドを使って、
- 1つの共有条件変数の、プライベート 'Any Ptr' 静的メンバー・フィールドを使って、
- 独自のパブリック・メンバ手続き 'Sub()' を使って、対応するマルチ-スレッド用の、組み込み手続き(同じ手続き名)を呼び出します。上記3つのポインタの値整合性テストも含みます。 (3つの 'thread...()' メンバーSub の非静的手続き、および、4つの 'mutex...()' メンバーSub と 5つの 'cond...()' メンバーSub の静的手続き)、
- ユーザー派生型内から、別の 'Sub()' によって上書きされる、抽象プライベート 'Sub()' スレッドで(したがって、静的アドレスは、オブジェクトの仮想テーブルで利用可能であり、参照により渡される隠された 'This' パラメーターは、スレッドに渡される 'Any Ptr' パラメータと互換性があります)。

#include once "fbthread.bi"
Type threadUDT Extends Object
    Public:
        Declare Sub ThreadCreate ()
        Declare Sub ThreadWait ()
        Declare Sub threadDetach ()
        Declare Static Sub MutexCreate ()
        Declare Static Sub MutexLock ()
        Declare Static Sub MutexUnlock ()
        Declare Static Sub MutexDestroy ()
        Declare Static Sub CondCreate ()
        Declare Static Sub CondWait ()
        Declare Static Sub CondSignal ()
        Declare Static Sub CondBroadcast ()
        Declare Static Sub CondDestroy ()
    Private:
        Declare Abstract Sub thread ()
        Dim As Any Ptr pThread
        Static As Any Ptr pMutex
        Static As Any Ptr pCond
End Type
Dim As Any Ptr threadUDT.pMutex
Dim As Any Ptr threadUDT.pCond

Sub threadUDT.ThreadCreate ()
    If This.pThread = 0 Then
      This.pThread = .ThreadCreate(Cast(Any Ptr Ptr Ptr, @This)[0][0], @This)
    End If
End Sub

Sub threadUDT.ThreadWait ()
    If This.pThread > 0 Then
        .ThreadWait(This.pThread)
        This.pThread = 0
    End If
End Sub

Sub threadUDT.threadDetach ()
    If This.pThread > 0 Then
        .Threaddetach(This.pThread)
        This.pThread = 0
    End If
End Sub
 
Sub threadUDT.MutexCreate ()
    If threadUDT.pMutex = 0 Then
        threadUDT.pMutex = .MutexCreate
    End If
End Sub
 
Sub threadUDT.MutexLock ()
    If threadUDT.pMutex > 0 Then
        .MutexLock(threadUDT.pMutex)
    End If
End Sub

Sub threadUDT.MutexUnlock ()
    If threadUDT.pMutex > 0 Then
        .MutexUnlock(threadUDT.pMutex)
    End If
End Sub

Sub threadUDT.MutexDestroy ()
    If threadUDT.pMutex > 0 Then
        .MutexDestroy(threadUDT.pMutex)
        threadUDT.pMutex = 0
    End If
End Sub

Sub threadUDT.CondCreate ()
    If threadUDT.pCond = 0 Then
        threadUDT.pCond = .CondCreate
    End If
End Sub

Sub threadUDT.CondWait ()
    If threadUDT.pCond > 0 And threadUDT.pMutex > 0 Then
        .CondWait(threadUDT.pCond, threadUDT.pMutex)
    End If
End Sub

Sub threadUDT.CondSignal ()
    If threadUDT.pCond > 0 And threadUDT.pMutex > 0 Then
        .CondSignal(threadUDT.pCond)
    End If
End Sub

Sub threadUDT.CondBroadcast ()
    If threadUDT.pCond > 0 And threadUDT.pMutex > 0 Then
        .CondBroadcast(threadUDT.pCond)
    End If
End Sub

Sub threadUDT.CondDestroy ()
    If threadUDT.pCond > 0 Then
        .CondDestroy(threadUDT.pCond)
        threadUDT.pCond = 0
    End If
End Sub

ページの頭に戻る



12. FreeBASIC で TLS(Thread Local Storage) をエミュレートできますか?
静的変数は通常、すべてのスレッドで共有されます。静的変数を変更すると、すべてのスレッドからその変更が見えるようになります。
通常の静的変数とは異なり、TLS 静的変数を作成した場合、すべてのスレッドはその変数のコピー(ただし、同じアクセス名)を持たなければなりません。 つまり、その変数への変更は、スレッドにローカル(ローカルに保存)になります。
これにより、スレッドセーフな手続きを作成することができます。なぜなら、この手続きへの各呼び出しは、宣言された同じ静的変数のコピーを持っているからです。
静的変数を持つ通常の手続きでは、その変数の内容は複数のスレッドによって更新される可能性があります。しかし、TLS では、これらを各スレッドにローカルな静的データと考えることができます。

TLS データは静的データと似ていますが、唯一の違いは、TLS データは各スレッドに固有のデータだということです。

この FreeBASIC 用 TLS エミュレーションの原理は、要求された各 TLS 変数に対して静的な配列を使い、各スレッドが配列要素にアクセスするための独自のインデックス(隠し)を持つというものです。
スレッドに関連するこの独自のインデックスは、スレッドハンドルの値から推測されます:
- fbc のバージョン >= 1.08 では、スレッドハンドルの値は、任意のスレッドから呼び出される 'Threadself()' 関数 (新関数) から単純に返されます。
- fbc のバージョンが 1.08 未満の場合は、コードはより複雑になります:
- スレッドハンドルの値は、作成時に親(またはメイン)スレッドの 'ThreadCreate()' のリターンからだけアクセスできます。
- 'Threadself()' 関数を適切にエミュレートする方法はなく、ひねった方法だけが使えます。
- 以下の例(fbc version < 1.08 用)では、'Threadself()' 関数(参照で返す)の値は、スレッドが使用するたびに(独自のスレッドハンドルで)初期化され、対応する 'ThreadCreate()' と同様に、このすべて(初期化+使用)が mutex で保護されています。

以下の例では、TLS 静的変数は、単一の一般的なカウント手続き(パラメーターを渡さない 'counter()')で使われる整数です。 このカウント手続きは、各スレッドによって呼び出されます(したがって、各スレッドは、同じ単一のカウント手続きを呼び出しますが、互いに独立してカウントします)。
1つのマクロで、任意の型の TLS 変数(配列を除く)を定義できます。
ページの頭に戻る



13. FreeBASIC でスレッド・プーリング機能をエミュレートできますか?
ページの頭に戻る

参照
プログラマーのための案内に戻る
←リンク元に戻る プログラム開発関連に戻る
ページ歴史:2022-06-23 02:33:04
日本語翻訳:WATANABE Makoto、原文著作者:fxm

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

表示-非営利-継承