マルチスレッド・アプリケーションの開発をサポートするために,Tru64 UNIX オペレーティング・システムでは,POSIX Threads Library (Compaq Multithreading Run-Time Library) を提供しています。 POSIX Threads Library インタフェースは,いくつかの拡張機能を加えた,IEEE 規格 1003.1c-1995 スレッド (POSIX P1003.1C スレッドとも呼ぶ) の Tru64 UNIX のインプリメンテーションです。
実際のスレッド・インタフェースのほかに,オペレーティング・システムでは TIS (Thread-Independent Services: スレッド独立型サービス) を提供しています。 TIS ルーチンは,独自のスレッドを作成しない効率的なスレッド・セーフ・ライブラリの作成を支援します (TIS ルーチンについての詳細は,12.4.1 項を参照)。
この章では,次の項目について説明します。
Tru64 UNIX におけるマルチスレッド・サポートの概要 (12.1 節)
POSIX 準拠のための実行時ライブラリの変更 (12.2 節)
スレッド・セーフおよびスレッド・リエントラント・ルーチンの特性 (12.3 節)
スレッド・セーフ・コードの作成方法 (12.4 節)
マルチスレッド・アプリケーションの作成方法 (12.5 節)
スレッドとは,プログラム内における単一のシーケンシャルな制御の流れのことです。 複数のスレッドが同時に実行され,アドレス空間をはじめ,所有しているプロセスのほとんどのリソースを共用します。 省略時の設定では,プロセスには最初に 1 つのスレッドがあります。
複数のスレッドは,次のような目的で使用されます。
マルチプロセッサ・システム上で実行するアプリケーションの性能改善
特定のプログラミング・モデル (たとえば,クライアント/サーバ・モデル) の実現
低速デバイス処理のカプセル化と分離
また,複数のスレッドを特定のイベント管理の代替方法として使用することもできます。
たとえば,select()
あるいは
poll()
システム・コールを使用して,複数のファイル記述子への同時 I/O オペレーションを管理する代わりに,各プロセスのファイル記述子ごとに 1 つのスレッドを使用することもできます。
Tru64 UNIX システムに対するマルチスレッド開発環境の構成要素は次のとおりです。
コンパイラ・サポート
cc
または
c89
コマンドで
-pthread
オプションを使用してコンパイルします。
スレッド・パッケージ
スレッド・セーフ・サポート・ライブラリ
スレッド・セーフ・サポート・ライブラリには,libaio
,
libcfg
,
liblmf
,
libm
,
libmsfs
,
libpruplist
,
libpthread
,
librt
, および
libsys5
が含まれます。
ladebug
デバッガ
prof
プロファイラ および
gprof
プロファイラ
libprof1_r.a
プロファイリング・ライブラリを使用するためには,prof
に対しては
-p
および
-pthread
オプションを,gprof
に対しては
-pg
および
-pthread
オプションを指定してコンパイルします。
atom
ベースの
pixie
,hiprof
,third
ツール
マルチスレッド・アプリケーションのプロファイリングについては,8.8 節を参照してください。
ロジックおよび性能の潜在的な問題について,マルチスレッド・アプリケーションを分析するには,Visual Threads (「Associated Products Volume 1」CD-ROM から利用可能) を使用することができます。
Visual Threads は,POSIX Threads Library アプリケーションおよび Java アプリケーションで使用することができます。
12.2 POSIX 準拠のための実行時ライブラリの変更
DEC OSF/1 オペレーティング・システム (DIGITAL UNIX Version 4.0 より古いバージョン) のリリースでは,別個のリエントラント・ルーチン (*_r
ルーチン) を多数提供して,C 実行時ライブラリ内の静的データの問題 (最初の 2 つの問題については12.3.1 項を参照) を解決していました。
Tru64 UNIX オペレーティング・システムのリリースでは,静的データをスレッド固有のデータと置き換えることによって,リエントラントでないルーチンの静的データの問題を解決しています。
POSIX 1003.1c で指定する数個のルーチンを除き,Tru64 UNIX システムでは代替ルーチンは必要なく,バイナリ互換性のみのために保持されています。
POSIX 1003.1c で指定されている代替スレッド・セーフ・ルーチンは次の関数だけであり,スレッド・セーフなコードを記述する際には必要です。
asctime_r * |
ctime_r * |
getgrgid_r * |
getgrnam_r * |
getpwnam_r * |
getpwuid_r * |
gmtime_r * |
localtime_r * |
rand_r * |
readdir_r * |
strtok_r |
DIGITAL UNIX バージョン 4.0 からは,前述の一覧でアスタリスク (*) の付いているインタフェースは,POSIX 1003.1c に準拠する新しい定義になっています。
これらのルーチンの旧バージョンは,プリプロセッサ・シンボル
_POSIX_C_SOURCE
に値
199309L
(POSIX 1003.1b 準拠を示す -- ただし,これを行うと POSIX 1003.1c スレッドが無効になる) を指定して定義することにより取得できます。
これらのルーチンの新バージョンは,DIGITAL UNIX バージョン 4.0 以降でコードをコンパイルしたときの省略時の設定ですが,各ルーチンのリファレンス・ページで指定されているヘッダ・ファイルをインクルードすると確実です。
スレッドを使用してのプログラミングについては,『Guide to the POSIX Threads Library』 および
cc
(1)monitor
(3)prof
(1)gprof
(1)12.3 スレッド・セーフ・ルーチンおよびリエントラント・ルーチンの特性
ライブラリ内のルーチンは,スレッド・セーフであっても,スレッド・セーフでなくても構いません。 スレッド・セーフなルーチンは,複数のスレッドから,スレッド間の望ましくない相互作用なしで,同時に呼び出すことができるルーチンです。 ルーチンは,次のいずれかの理由によってスレッド・セーフのことがあります。
本質的にリエントラントである。
ミューテックスでロックやスレッド固有のデータを使用する。
ミューテックスは,複数のスレッドが共用データへのアクセスを直列化できるようにするために使用される同期オブジェクトです。
リエントラント関数は,複数のスレッドからの同時呼び出しで,状態を共用しません。 リエントラント・ルーチンは理想的なスレッド・セーフのルーチンですが,すべてのルーチンがリエントラントとして作成できるわけではありません。
DIGITAL UNIX バージョン 4.0 より前では,C 実行時ライブラリ (libc
) ルーチンの多数はスレッド・セーフではなく,これらのルーチンの代替バージョンが
libc_r
で提供されていました。
DIGITAL UNIX バージョン 4.0 からは,以前
libc_r
で提供されていたすべての代替バージョンは,libc
にマージされました。
スレッド・セーフ・ルーチンとそれに対応するスレッド・セーフでないルーチンが同じ名前を持っている場合は,スレッド・セーフでないルーチンが置き換えられました。
スレッド・セーフのルーチンは TIS ルーチンを使用するように変更されています (12.4.1 項を参照)。
これは,シングル・スレッドの場合に広範囲にわたるオーバヘッドなしで,シングル・スレッドおよびマルチスレッドの両方の環境で動作します。
12.3.1 スレッド・セーフでないコーディング例
コードをスレッド・セーフにしないようにする方法は,DIGITAL UNIX バージョン 4.0 より前にスレッド・セーフでなかった
libc
関数のいくつかを調べるとわかります。
ポインタを単一の静的に割り当てられたバッファに返す
この問題の一例として,
ctime
(3)
char *ctime(const time_t *timer);
この関数は出力引数をとらず,ポインタを静的に割り当てられたバッファに返します。 このバッファには,関数の単一パラメータに指定された時間の ASCII 表現による文字列が含まれています。 単一の静的に割り当てられたバッファがこの目的に使用されるため,この関数を呼び出す他のスレッドは,以前に呼び出したスレッドに返す文字列を重ね書きします。
ctime()
ルーチンをスレッド・セーフにするため,POSIX P1003.1c 規格では代替バージョン
ctime_r()
を定義して,それが追加の出力引数をとるようにしました。
この引数は呼び出し側で割り当てられるユーザ提供のバッファです。
ctime_r()
関数は,次のような ASCII 時間文字列をバッファに書き込みます。
char *ctime_r(const time_t *timer, char *buf);
この関数のユーザは,buf
引数によって占有されているストレージが,別のスレッドで使用されないように確認する必要があります。
内部状態の維持
この問題の一例は,rand()
関数です。
void srand(unsigned int seed); int rand(void);
この関数は,単純な擬似乱数ジェネレータです。
srand()
関数で設定した任意の開始
seed
値に対して,擬似乱数の同一シーケンスを生成します。
これを行うには,各呼び出しで更新された状態値を保持しておきます。
別のスレッドでこの関数が呼び出されると,任意の開始シードに対して 1 つのスレッド内で返される数のシーケンスが,決定論的でなくなります。
これは望ましくありません。
この問題を回避するため,2 番目のインタフェース
rand_r()
が POSIX 1003.1c で指定されました。
このインタフェースは追加の引数をとり,rand_r()
が乱数ジェネレータの状態を保持するために使用するユーザ提供の整数へのポインタを指定できるようにします。
int rand_r(unsigned int *seed);
この関数のユーザは,seed
引数が別のスレッドによって使用されることのないように確認する必要があります。
スレッド固有データを使用することは,これを行う 1 つの方法です (12.4.1.2 項を参照)。
スレッド間で共用する読み取り/書き込みデータ項目に対するオペレーション
読み取り/書き込みデータ共用の問題は,ミューテックスを使用することにより解決できます。 この場合,ルーチンはリエントラントとはみなされませんが,スレッド・セーフです。 スレッド固有のデータと同様,ミューテックス・ロックはルーチンのユーザに対して透過的です。
ミューテックスはいくつかの
libc
ルーチンで使用されますが,最も一般的なものは
stdio
ルーチン,たとえば,printf()
です。
stdio
ルーチンのミューテックス・ロックはストリームによって行われますが,これは,2 つのプロセスが同時に 1 つのストリーム・バッファに充てんしようとした場合に,ストリーム上での同時オペレーションが衝突するのを防ぎます。
ミューテックス・ロックはまた,fopen()
や
fclose()
などのオペレーション中に,C 実行時ライブラリにある特定の内部データ・テーブルに対しても行われます。
これらのルーチンの代替バージョンは,アプリケーション・プログラミング・インタフェース (API) の変更が必要ないため,元のバージョンと同じ名前です。
ミューテックスの使用例については,12.4.3 項を参照してください。
シングル・スレッドおよびマルチスレッド・アプリケーションの両方で使用できるコードを作成するときには,スレッド・セーフな方法でコーディングする必要があります。 次のコーディング方法に従ってください。
静的な読み取り/書き込みデータは,削除するか,スレッド固有のデータに変換するか,あるいはミューテックスにより保護する。
C 言語では,静的読み取り専用データを
const
型修飾子で宣言すると,データの誤使用を減らすことができます。
グローバルな読み取り/書き込みデータは,削除するか,あるいはミューテックス・ロックにより保護する。
ファイル記述子のようなプロセスごとのシステム・リソースは,すべてのスレッドからアクセス可能であるため,注意して使用する。
グローバルな errno セルへの参照は,geterrno()
および
seterrno()
への呼び出しと置換する。
ソース・ファイルで
errno.h
が取り込まれ,次の条件のいずれかが当てはまる場合には,置換は必要ありません。
-pthread
オプション (cc
または
c89
コマンド) を使用して,ファイルがコンパイルされている。
ソース・ファイルの先頭で
pthread.h
ファイルが取り込まれている。
errno.h
ファイルを取り込む前に,_REENTRANT
プリプロセッサ・シンボルが明示的に設定されている。
他のスレッド・セーフでないライブラリまたはオブジェクト・ファイルへの依存要素の発生を防止する必要がある。
以下の項では,スレッド固有データに対して TIS (Thread Independent Services) を使用する方法について説明します。
12.4.1.1 TIS の概要
TIS (Thread Independent Services) は,C 実行時ライブラリによって提供されるルーチンのパッケージであり,シングル・スレッドおよびマルチスレッド・アプリケーションの両方に対して,効率的なコードを作成するために使用されます。 TIS ルーチンは,ミューテックスの処理,スレッド固有データの処理,およびその他のさまざまな目的で使用することができます。
シングル・スレッド・アプリケーションで使用されると,これらのルーチンは単純化された意味規則を使用して,シングル・スレッド用のスレッド・セーフ・オペレーションを実行します。 POSIX Threads Library が存在する場合は,ルーチン本体がより複雑なアルゴリズムで置き換えられて,マルチスレッド用に動作が最適化されます。
TIS を
libc
自体の内部で使用すると,1 つのバージョンの C 実行時ライブラリが,シングル・スレッドおよびマルチスレッド・アプリケーションの両方で使用できるようになります。
この機能の使用方法についての詳細は,『Guide to the POSIX Threads Library』 および
tis
(3)12.4.1.2 スレッド固有データの使用
例 12-1
は,シングル・スレッドおよびマルチスレッド・アプリケーションの両方で使用できる関数でスレッド固有のデータを使用する方法を示しています。
簡潔にするため,ほとんどのエラー・チェックは省いています。
例 12-1: スレッド・プログラム例
#include <stdlib.h> #include <string.h> #include <tis.h> static pthread_key_t key; void _ _init_dirname() { tis_key_create(&key, free); } void _ _fini_dirname() { tis_key_delete(key); } char *dirname(char *path) { char *dir, *lastslash; /* * Assume key was set and get thread-specific variable. */ dir = tis_getspecific(key); if(!dir) { /* First time this thread got here. */ dir = malloc(PATH_MAX); tis_setspecific(key, dir); } /* * Copy dirname component of path into buffer and return. */ lastslash = strrchr(path, '/'); if(lastslash) { memcpy(dir, path, lastslash-path); dir[lastslash-dir+1] = '\0'; } else strcpy(dir, path); return dir; }
次の TIS ルーチンが前述の例で使用されています。
tis_key_create
一意なデータ・キーを生成します。
tis_key_delete
データ・キーを削除します。
tis_getspecific
指定されたキーに関連するデータを取得します。
tis_setspecific
指定されたキーに関連するデータ値を設定します。
_ _init_
および
_ _fini_
ルーチンは,この例ではスレッド固有のデータ・キーを初期化して破壊するために使用されています。
このオペレーションは 1 度だけ行われ,これらのルーチンは,ライブラリが
dlopen()
でロードされている場合にも,このことを確実に示す便利な方法を提供します。
_ _init_
および
_ _fini_
ルーチンの使用方法についての説明は,
ld
(1)
スレッド固有のデータ・キーは,実行時に POSIX Threads Library によって提供される限定リソースです。
多数のデータ・キーを使用する必要のあるライブラリは,1 つのデータ・キーだけを作成して,別々のデータ項目をすべて,構造体あるいは,そのキーで指し示されるポインタの配列として保存するようにライブラリをコーディングします。
12.4.2 TLS (Thread Local Storage) の使用
C コンパイラでは,TLS (Thread Local Storage) のサポートは常に有効になっています (cc
コマンドの
-ms
オプションは不要です)。
C++ では,TLS は
-ms
オプションを指定したときのみ認識され,指定していないときはエラーとして処理されます。
TLS は,マルチスレッド・プロセスのスレッドが存在する期間に静的エクステントを持ち (スタック上ではない),スレッドごとに割り当てられるデータ領域です。
標準のマルチスレッド・プログラムでは,静的エクステント・データは,プロセスのすべてのスレッドで共有されますが,TLS は各スレッドごとに割り当てられ,各スレッドにはそれぞれ独自にデータのコピーがあり,スレッドがそのデータを変更しても,プロセス内の他のスレッドから見える値には影響を与えないようになっています。 スレッドについての詳細は,『Guide to the POSIX Threads Library』 を参照してください。
TLS の主要な機能は,POSIX (POSIX Threads Library)
の pthread_key_create()
,pthread_setspecific()
,pthread_getspecific()
,pthread_key_delete()
のようなアプリケーション・プログラミング・インタフェース (API) によって提供されてきました。
これらの API は POSIX 準拠のプラットフォーム間での移植性がありますが,使いにくく間違いが起こりやすくなることがあります。
また,適切な
static
および
extern
変数宣言とその使用をすべて,スレッド・ローカル API の呼び出しに置き換えて,既存のシングルスレッド・コードをスレッド・セーフにするには,通常,かなりの技術的作業が必要になります。
さらに,Windows-32 プラットフォームでは API のセット (TlsAlloc()
,TlsGetValue()
,TlsSetValue()
,TlsFree()
) が少し異なっており,POSIX API の場合と同じような使用上の問題があります。
これに対して,TLS の言語機能はいずれの API よりも使い方が簡単で,シングルスレッド・コードをマルチスレッド・コードに変換する際は特に便利です。
これは,static
または
extern
変数がスレッド固有の値を持つように変更するには,宣言に記憶クラス修飾子を追加するだけでよいからです。
コンパイラ,リンカ,プログラム・ローダ,デバッガは,この修飾子で宣言された変数に対して,複雑な API 呼び出しを自動的に効率良く実現します。
API によるコーディングとは異なり,変数の使用をすべて探して変更したり,明示的に割り当ておよび割り当て解除コードを追加する必要はありません。
この言語機能は,正式なプログラミング標準では一般に移植性がありませんが,Tru64 UNIX と Windows-32 プラットフォームの間では移植性があります。
12.4.2.1 _ _thread 属性
Tru64 UNIX の C および C++ コンパイラには,拡張記憶クラス属性,_ _thread
が含まれます。
スレッド変数を宣言するには,_ _thread
属性を
_ _declspec
キーワードとともに使用しなければなりません。
たとえば,次のコードは整数のスレッド・ローカル変数を宣言し,それを値で初期化しています。
_ _declspec( _ _thread ) int tls_i = 1;
スレッド・ローカルのオブジェクトおよび変数を宣言する際は,以下のガイドラインと制限を守らなければなりません。
記憶クラス属性の
_ _thread
は,データの宣言および定義にのみ適用できます。
関数の宣言や定義には使用できません。
たとえば,次のコードではコンパイラ・エラーが発生します。
#define Thread _ _declspec( _ _thread ) Thread void func(); // Error
_ _thread
属性は,記憶域の存続期間が静的なデータにのみ指定できます。
これには,グローバル・データ・オブジェクト (static
と
extern
の両方) と,ローカルな
static
オブジェクト,C++ クラスの
static
データ・メンバがあります。
自動またはレジスタのデータ・オブジェクトには
_ _thread
属性は宣言できません。
たとえば,次のコードではコンパイラ・エラーが発生します。
#define Thread _ _declspec( _ _thread ) void func1() { Thread int tls_i; // Error } int func2( Thread int tls_i ) // Error { return tls_i; }
スレッド・ローカル・オブジェクトの宣言と定義では,その宣言と定義が同じファイルにあるか異なるファイルにあるかにかかわらず,_ _thread
属性を使用しなければなりません。
たとえば,次のコードではエラーが発生します。
#define Thread _ _declspec( _ _thread ) extern int tls_i; // This generates an error, because the int Thread tls_i; // declaration and the definition differ.
_ _thread
属性は,タイプ修飾子としては使用できません。
たとえば,次のコードではコンパイル時にエラーが発生します。
char _ _declspec( _ _thread ) *ch; // Error
スレッド・ローカル・オブジェクトのアドレスは,リンク時の定数とは見なされず,このようなアドレスを含む式は定数式とは見なされません。 標準 C では,静的またはスレッド・ローカルなエクステントを持つオブジェクトの初期値式として,スレッド・ローカル変数のアドレスを使用できないという影響があります。 たとえば,ファイルの範囲で出現した場合,次のコードは C コンパイラではエラーになります。
#define Thread _ _declspec( _ _thread ) Thread int tls_i; int *p = &tls_i; // ERROR
標準の C では,オブジェクトまたは変数を,自分への参照を含む式で初期化することが許されていますが,静的でないエクステントのオブジェクトに限られています。 通常,C++ では,自分への参照を含む式で動的にオブジェクトを初期化することが許されていますが,このような初期化は,スレッド・ローカル・オブジェクトでは許されていません。 たとえば次のようになります。
#define Thread _ _declspec( _ _thread ) Thread int tls_i = tls_i; // C and C++ error int j = j; // Okay in C++; C error Thread int tls_i = sizeof( tls_i ) // Okay in C and C++
現在初期化しようとしているオブジェクトを含む
sizeof
式は,自分への参照とは見なされないため,C および C++ では許されることに注意してください。
12.4.3 スレッド間でデータを共用するためのミューテックス・ロックの使用
場合によっては,静的データをスレッド・セーフ・コードに変換するために,スレッド固有のデータを使用することは有効ではありません。
たとえば,データ・オブジェクトがスレッド間で共用される (libc
内の
stdio
ストリームのように) 場合には,スレッド固有のデータは使用すべきではありません。
プロセス毎のリソースの操作も,スレッド固有データが不適切な場合の例です。
次の例は,スレッド・セーフな方法でプロセス毎のリソースを操作する方法を示しています。
#include <pthread.h> #include <tis.h> /* * NOTE: The putenv() function would have to set and clear the * same mutex lock before it accessed the environment. */ extern char **environ; static pthread_mutex_t environ_mutex = PTHREAD_MUTEX_INITIALIZER; char *getenv(const char *name) { char **s, *value; int len; tis_mutex_lock(&environ_mutex); len = strlen(name); for(s=environ; value=*s; s++) if(strncmp(name, value, len) == 0 && value[len] == '=') { tis_mutex_unlock(&environ_mutex); return &(value[len+1]); } tis_mutex_unlock(&environ_mutex); return (char *) 0L; }
この例では,環境にアクセスする前にロックが 1 度設定され (tis_mutex_lock
),リターンする前に 1 度だけロックが解除されている (tis_mutex_unlock
) ことに注意してください。
マルチスレッドの場合には,最初のスレッドがロックを保持している間に他のスレッドがその環境にアクセスしようとすると,最初のスレッドがロック解除を実行するまで,他のスレッドはブロックされます。
シングル・スレッドの場合には,ロックとロック解除のシーケンスにコーディング・エラーが存在しない限り,競合は起こりません。
マルチスレッド・アプリケーションで,ロック状態を
fork()
システム・コールの呼び出し中にも有効なままにしておく必要がある場合は,pthread_atfork()
ハンドラ関数を作成および登録して,fork()
呼び出しの前にそのロックを設定し,fork()
呼び出しの後で子および親の両方でそのロックを解除する方法が有効です。
これにより,別のスレッドがロックを保持している間に,別のスレッドがフォーク操作を行うことがなくなります。
ロックが別のスレッドによって保持されている場合には,フォーク操作によって 1 つのスレッドだけを持つ子が作成されるため,子で永久にロックされることになります。
独立したライブラリの場合には,pthread_atfork()
への呼び出しは,そのライブラリの
_ _init_
ルーチンで行われます。
ほとんどの Pthread ルーチンと異なり,pthread_atfork
ルーチンは
libc
で使用可能であり,シングル・スレッドおよびマルチスレッド・アプリケーションの両方で使用することができます。
12.5 マルチスレッド・アプリケーションの作成
マルチスレッド・アプリケーションのコンパイルおよびリンクは,シングル・スレッド・アプリケーションのコンパイルおよびリンクとは多少異なります。
以下の項では,この違いについて説明します。
12.5.1 マルチスレッド C アプリケーションのコンパイル
アプリケーションがシングル・スレッドかあるいはマルチスレッドかによって,多くのシステム・ヘッダ・ファイルは,アプリケーションのコンパイルでインクルードされる際に,異なる定義のセットを提供します。
コンパイラが,シングル・スレッドあるいはスレッド・セーフのどちらの動作を生成するかは,プリプロセッサ・シンボル
_REENTRANT
が定義されているかどうかによって決まります。
cc
または
c89
コマンドに
-pthread
オプションを指定すると,_REENTRANT
シンボルが自動的に定義されます。
また,pthreads.h
ヘッダ・ファイルがインクルードされている場合も定義されます。
Pthread ライブラリ
libpthread.so
を使用するアプリケーションでは,このヘッダ・ファイルを最初にインクルードする必要があります。
-pthread
オプションは,C プログラムのコンパイルに対してはその他の影響は与えません。
C コンパイラによって作成されるコードのリエントラント性は,特定のオプションではなく,プログラマによる適切なリエントラント・コーディングの使用,スレッド・セーフ・サポート・ルーチンおよび関数のみの使用によって決まります。
12.5.2 マルチスレッド C アプリケーションのリンク
マルチスレッド C アプリケーションをリンクする場合は,-pthread
オプションを指定して
cc
または
c89
コマンドを使用します。
-pthread
オプションは,リンク時に次の方法でライブラリ探索パスの修正に影響を与えます。
Pthread ライブラリをリンクにインクルードする。
例外ライブラリをリンクにインクルードする。
-l
オプションで指定したライブラリに対しては,対応するスレッド・セーフ・ルーチンの名前の末尾に
_r
が付いたライブラリの配置および探索を行う。
-pthread
オプションは,リンカの動作に対してはその他の影響は与えません。
リンクされたコードのリエントラント性は,元のコードにおける適切なリエントラント・コーディングの使用,あるいは適切なヘッダ・ファイルあるいはライブラリによるコンパイルおよびリンクによって決まります。
12.5.3 その他の言語のマルチスレッド・アプリケーションの作成
すべてのコンパイラがリエントラント・コードを生成するとは限りません。 言語によっては困難な場合もあります。 また,アプリケーションにリンクされる実行時ライブラリがすべてスレッド・セーフであることも必要となります。 詳細については,使用するコンパイラのマニュアルおよび実行時ライブラリのマニュアルを参照してください。