スレッドの識別

スレッドを、アプリケーション内で作成された他のスレッドと区別すると役立つことがあります。たとえば 4 つのスレッドをもつアプリケーションで、2 つのスレッドが作成側および使用する側の関係にある場合には、お互いのスレッドのスレッド ハンドルを知ることは、これら連動する 2 つのスレッドにとって役に立ちます。これらのハンドルを取得した後は、CBL_THREAD_SUSPEND および CBL_THREAD_RESUME の呼び出しのみを使用してすべての同期を取ることができます。アプリケーション内の各スレッドが、その機能に関連する名前を作成し、その名前をスレッド ハンドルに関連付けると、連動するスレッドはスレッド名のリストを検索し、お互いのハンドルを検出できます。

グローバルにアクセス可能なデータを各スレッドとそのハンドルに関連付けると、終了フラグを保持し、CBL_THREAD_KILL を使用する可能性を未然に防ぐことができます。アプリケーションの各スレッドは、終了要求を確認するためにその終了フラグをポーリングできます。終了するスレッドは、正常に終了する前に、ロックが保持されていないことと、アクションの同期を取る必要がないことを確認します。

スレッドのグローバルにアクセス可能なデータは、スレッド内で CBL_THREAD_IDDATA_ALLOC ルーチンを実行するとスレッド ハンドルに関連付けられます。スレッド ハンドルがすでに認識されている場合は、CBL_THREAD_IDDATA_GET ルーチンを使用してこのデータを取得します。一方、スレッド ハンドルが認識されていない場合は、CBL_THREAD_LIST_n ルーチンを使用してこのデータを取得します。

例 - グローバルにアクセス可能なデータとスレッド ハンドルの関連付け

次の例では、スレッド内で CBL_THREAD_IDDATA_ALLOC を呼び出して、グローバルにアクセス可能なデータをスレッド ハンドルに関連付ける方法を示します。スレッド ハンドルがすでに認識されている場合は、CBL_THREAD_IDDATA_GET を呼び出してデータを取得します。スレッド ハンドルが認識されていない場合は、CBL_THREAD_LIST_n ルーチンを使用してこのデータを取得します。

      *************** MAINPROG.CBL ************************
       identification division.
       program-id. mainprog.
      
       Data Division.
       Local-Storage Section.
       01 ret-wait         usage pointer.
       01 iddata-ptr       usage pointer.
       01 sub-iddata-ptr   usage pointer.
       01 sub-handle       usage thread-pointer.
       Linkage Section.
        01 iddata-record.
           05 iddata-name  pic x(20).
         05 iddata-term    pic x comp-x value 0.
      
       Procedure Division.
      
      * Establish identification data - don't provide 
      * initialization data when it is allocated, instead
      * initialize it after the pointer is retrieved.
      
           call 'CBL_THREAD_IDDATA_ALLOC' using 
                                          by value zero
                                          length of iddata-record
           call 'CBL_THREAD_IDDATA_GET'   using iddata-ptr
                                                omitted
           set address of iddata-record to iddata-ptr
           move 'main' to iddata-name
      
      * Create sub-thread
      
           start 'SUBPROG '  identified by sub-handle
      
      * Wait until child creates its iddata and then flag 
      * termination
      
           set sub-iddata-ptr to NULL
           perform until 1 = 0
               call 'CBL_THREAD_IDDATA_GET' using sub-iddata-ptr
                                            by value sub-handle
               if sub-iddata-ptr not = null
                   exit perform
               end-if
               call 'CBL_THREAD_YIELD'
           end-perform
           set address of iddata-record to sub-iddata-ptr
           move 1 to iddata-term
      
      * Wait until the child ends
           wait for sub-handle   *> clear up thread's resources

           display 'All synchronization is complete on ' &
                   'RTS termination'
           stop run.
       end program mainprog.

      *************** SUBPROG.CBL ************************

       identification division.
       program-id. subprog.
      
       Data Division.
       Working-Storage Section.
       01 sub-iddata.
          05 sub-name	          pic x(20) value 'sub'.
          05 sub-term	          pic x comp-x value 0.
       Local-Storage Section.
       01 iddata-ptr            usage pointer.
       01 thread-handle         usage pointer.
       01 thread-state          pic x(4) comp-x.
       01 parent-handle         usage pointer.
       Linkage Section.
       01 iddata-record.
          05 iddata-name        pic x(20).
          05 iddata-term        pic x comp-x value 0.
       Procedure Division.
      
      * Establish identification data - provide 
      * initialization data
           call 'CBL_THREAD_IDDATA_ALLOC'
                                using sub-iddata
                                by value length of sub-iddata
      
      * Find our parent thread and resume him
      
           call 'CBL_THREAD_LIST_START'		
                             using thread-handle
                                   thread-state
                                   iddata-ptr
           set parent-handle to NULL
           perform until thread-handle = null 
                      or return-code not = 0
               if iddata-ptr not = null
                   set address of iddata-record  to iddata-ptr
                   if iddata-name = 'main'
                       set parent-handle to thread-handle
                       exit perform
                  end-if
               end-if
               call 'CBL_THREAD_LIST_NEXT' using thread-handle
                                                 thread-state
                                                 iddata-ptr
           end-perform
           call 'CBL_THREAD_LIST_END'
           if parent-handle = NULL
               display 'synchronization error'
               stop run
           end-if
           call 'CBL_THREAD_IDDATA_GET' using iddata-ptr
                                              omitted
           set address of iddata-record to iddata-ptr
           perform until iddata-term = 1
               call 'CBL_THREAD_YIELD'
           end-perform
           exit program.
       end program subprog.    

この例では、スレッドとアプリケーションを終了するためのハンドシェイクを確立します。このようなハンドシェイクは、主スレッドのハンドルをパラメーターとして子スレッドに渡すことで、より簡単に行えることに注目してください。この方法を使用すると、識別データに依存したり、スレッド リストをステップ実行したりする必要はありません。

このサンプルについては、次の点に注意してください。

  • スレッド識別データは、次の 2 つの方法で作成されます。
    • 親スレッドでは、データは初期化されていない状態で作成されます。そして、スレッドは iddata ポインターを取得し、割り当て済みのメモリの領域を初期化します。
    • 子スレッドは、初期化済みのデータを提供します。これにより、アプリケーションが iddata の割り当てと初期化を行う間に実行ウィンドウが表示されなくなります。

    アプリケーションで使用する方法は、識別データで予測される競合のレベルによって異なります。

  • 親スレッド内のループは、子スレッドが識別データを作成するのを待機し、子スレッド内のループは、親スレッドが終了フラグを設定するのを待機します。CBL_THREAD_YIELD を呼び出すと、これらのループがハードウェアのビジー状態で待機するのを回避できますが、イベント同期オブジェクトまたは条件同期オブジェクトを使用してコードを作成することをお勧めします。ネイティブ コードでは、代わりに CBL_THREAD_SUSPEND と CBL_THREAD_RESUME を使用できます。
  • CBL_THREAD_LIST_n ルーチンにより、スレッドは、ランタイム システムで認識されているすべてのスレッドをステップ実行し、スレッド ハンドル、スレッド状態、識別データ ポインターを取得できます。この例では、ハンドルおよび識別データ ポインターのみを使用していますが、状態情報も有益です。状態情報により、対象のスレッドが、デタッチされたスレッド、中断されたスレッド、または他言語のスレッドであるかどうかを呼び出し元のスレッドに通知できるためです。

    スレッドが CBL_THREAD_LIST_n ルーチンを使用する場合:

    • そのスレッドでは、使用できる他の CBL_THREAD_n ルーチンが限定されます。
    • 他のスレッドでは、CBL_THREAD_n ルーチンを使用できず、他の一部のライブラリ ルーチンの使用が制限されます。

    このため、次を行うことをお勧めします。

    • リストのステップ処理中に実行されるコードの量をできるだけ少なくします。
    • リストがロックされている間は、ファイル入出力またはユーザー入出力を行わないようにします。

    これらの制限は、CBL_THREAD_LIST_END の呼び出しによってリストのステップ実行が終了してはじめて解除されます。