スレッドのキャンセル

CBL_THREAD_CREATE で作成されたスレッドは、CBL_THREAD_KILL ルーチンで取り消すことができます。別の方法で作成されたスレッドを、CBL_THREAD_KILL を使用して強制終了することはできません。この場合には、COBOL スレッドはただちに異常終了されますが、通常は、CBL_THREAD_KILL を一般的なアプリケーション スレッド コントロールの一部として使用しないでください。主な理由は、スレッドの終了時に、ユーザーとシステムの同期リソースが、正しくロック解除または解放されないためです。そのため、ユーザー アプリケーションとランタイム システム間の同期が影響を受け、アプリケーションで重大な問題が発生することがあります。

CBL_THREAD_KILL は、主スレッドの重大エラー ハンドラーでは、適切に使用できます。このエラー ハンドラーでは、スレッド ハンドルを CBL_THREAD_LIST_n ルーチンによって獲得できます。スレッドをすべて取り消し、STOP RUN でアプリケーションを終了します。この方法は、同期プリミティブのロックが必要となる可能性を最小限にするため、CBL_THREAD_KILL のランダム使用ほどの危険性はありません。ただし実行単位の終了時に、ファイルの破損やデッドロックが生じる可能性はあります。

いずれにせよ、ほとんどのアプリケーションでは CBL_THREAD_KILL を使用しないようにしてください。そのためには、終了フラグをもつスレッド識別データを作成 (CBL_THREAD_IDDATA_ALLOC ルーチンを使用) します。そのデータは、ロックが保持されていないレベルの各スレッドでポーリングできます。終了フラグが設定されている場合は、ポーリング スレッドを正常に終了できます。

例 - スレッド識別データと終了フラグの作成

      ******************** MAINPROG.CBL ********************
       identification division.
       program-id. mainprog.
      
       Data Division.
       Local-Storage Section.
       01  ret-ptr                 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
      
      * Starting point
           call 'CBL_THREAD_CREATE' using   
                                 'SUBPROG '  
                        by value 0   *> No parameters
                                 0   *> Optional - parameter size
                                 0   *> Flag to create detached
                                 0   *> Default priority
                                 0   *> Default stack
                    by reference sub-handle
           if  return-code not = 0 
               display 'unable to create thread'
               stop run
           end-if
      
      * 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
               end-call
               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 "stop" to iddata-name
           move 1 to iddata-term
      
      * Wait till the child exits
           wait for sub-handle

           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を使用すると、ランタイム システムで認識されているすべてのスレッドをステップ実行できるようになり、スレッド ハンドル、スレッド状態、および識別データ ポインターを取得できます。この例では、ハンドルおよび識別データ ポインターのみを使用していますが、状態情報も有益です。状態情報により、対象のスレッドが、デタッチされたスレッド、中断されたスレッド、または他言語のスレッドであるかどうかを呼び出し元のスレッドに通知できるためです。

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

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

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

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

    この制限は、CBL_THREAD_LIST_END 呼び出しによりステップ実行が終了した場合にのみ解除されます。