1. 導入:なぜスレッド固有のロケータが必要か
メインフレームのマルチスレッド環境(CICSのタスク管理やバッチの並列処理など)において、最も頭を悩ませるのが「共有バッファの同時スキャン」です。複数のスレッドが同一のデータ領域を走査する際、ポインタ変数(ロケータ)を静的領域(STATIC)に置いてしまうと、スレッド間で現在位置が競合し、致命的なデータ不整合を引き起こします。本稿では、PL/I等の環境でスレッド固有空間(TLS: Thread Local Storage)を意識したロケータ配置を実現し、安全な並列処理を行う手法を解説します。
2. 基礎知識:AUTOMATIC変数とポインタの仕組み
メインフレーム言語(特にPL/I)において、変数の記憶域クラスは「どこにメモリが確保されるか」を決定します。
AUTOMATIC(AUTO)を指定すると、その変数はスタック(各スレッドの作業用メモリ)上に配置されます。ポインタ変数をAUTOにすることで、そのポインタ自体は各スレッド固有のものとなり、たとえ指し示す先のバッファが共通であっても、スキャン中の「現在地」を独立して管理することが可能になります。これが現代のJavaにおけるローカル参照の考え方の原点です。
3. 実装:スレッド固有のロケータ配置
実装の肝は、共有バッファ(STATICまたは外部定義)に対して、走査用ポインタ(ロケータ)を明示的に「AUTO」として宣言することです。これにより、ポインタのメモリ領域がスレッドスタックに隔離され、競合が排除されます。
4. サンプルプログラム:スレッドセーフなポインタ走査
以下は、共有バッファを各スレッドが独立して走査するためのPL/Iコード例です。
/ スレッド固有のロケータを用いた安全なスキャン /
PROCEDURE_SCAN: PROCEDURE;
/ 共有データ領域(全スレッド共通のバッファ) /
DCL SHARED_BUFFER CHAR(1000) STATIC EXTERNAL;
/ スレッド固有のポインタ(TLSに配置される) /
/ 各スレッドのスタックに生成されるため、他スレッドと競合しない /
DCL P_SCAN PTR AUTOMATIC;
/ 走査用の一時変数 /
DCL WORK_CHAR CHAR(1) BASED(P_SCAN);
/ ポインタを共有バッファの先頭へセット /
P_SCAN = ADDR(SHARED_BUFFER);
/ 1000バイトをスキャンする処理 /
DO I = 1 TO 1000;
/ ここでWORK_CHARを参照しても、P_SCANは自分専用のメモリにあるため /
/ 他のスレッドによる同時スキャンと衝突しない /
IF WORK_CHAR = ‘X’ THEN DO;
/ 処理ロジック /
END;
/ ポインタをインクリメント(次へ移動) /
P_SCAN = P_SCAN + 1;
END;
END PROCEDURE_SCAN;
5. 応用・注意点:現場で陥りやすい罠
・初期化の徹底
AUTOMATIC変数は、宣言直後の値が不定です。ポインタを使用する前に必ずADDR関数等で有効なアドレスを代入してください。これを怠ると、異常なメモリ領域を指し示し、S0C4(保護例外)を引き起こす原因となります。
・BASED変数のスコープ
ポインタ自体はAUTOでスレッド固有にできますが、ポインタが指し示す先(BASED変数)の構造体が、マルチスレッド環境で書き込み可能な状態になっていないか注意が必要です。スキャンだけでなく「更新」も伴う場合は、ロケータの分離だけでなく、ENQ/DEQ等の排他制御を併用することを推奨します。
・移行時の意識
Javaやモダン言語への移行を検討している場合、この「AUTOポインタ」の設計思想を理解していれば、マルチスレッドクラスのインスタンス変数とローカル変数の使い分けが非常にスムーズに設計できるようになります。

コメント