4.60 スレッドセーフではない C ライブラリ関数

以下の表は、スレッドセーフではない C ライブラリ関数の一覧です。

表 4-2 スレッドセーフではない関数

関数 説明
asctime()、localtime()、strtok()
これらの関数は、いずれもスレッドセーフではありません。各関数では、スタティックバッファが使用されます。スタティックバッファは、関数の呼び出しとその戻り値の使用の間に別のスレッドによって上書きされる可能性があります。
ARM では、再入可能バージョンとして、_asctime_r()_localtime_r()、および _strtok_r() が用意されています。安全性を確保するためにこれらの関数を使用することを推奨します。

これらの再入可能関数は、追加のパラメータを取ります。_asctime_r() は、追加のパラメータとして、出力文字列の書き込み先バッファを指すポインタを取ります。_localtime_r() は、追加のパラメータとして、結果の書き込み先 struct tm を指すポインタを取ります。_strtok_r() は、追加のパラメータとして、次のトークンを指す char ポインタを指すポインタを取ります。
exit()
マルチスレッドプログラムでは、exit() を呼び出さないで下さい(すべてのスレッドを停止する基礎となる _sys_exit() の実装を指定していても呼び出さないで下さい)。
これを守らないと、_sys_exit() が呼び出される前に exit() がクリアされ、他のスレッドに対する割り込みが発生します。
gamma()、lgamma()、lgammaf()、lgammal() a
これらの拡張 mathlib 関数はグローバル変数 _signgam を使用するため、スレッドセーフではありません。
mbrlen()、mbsrtowcs()、mbrtowc()、wcrtomb()、wcsrtombs()
C90 マルチバイト変換関数(stdlib.h に定義)は、すべてのスレッドにロックなしで共有される内部静的状態を保持しているため、スレッドセーフではありません。このような関数として、mblen() および mbtowc() があります。
ただし、拡張再起動可能バージョン(wchar.h に定義)である mbrtowc() および wcrtomb() は、独自に作成した mbstate_t オブジェクトを指すポインタを渡す場合にはスレッドセーフです。マルチバイト文字列を処理する場合にスレッドセーフティを確保するには、これらの関数を非 NULL mbstate_t * パラメータと一緒に排他的に使用する必要があります。
rand()、srand()
これらの関数は、保護されていないグローバルな内部状態を維持します。つまり、rand() への呼び出しはスレッドセーフではありません。
ARM では、以下のいずれかの方法を推奨します。
  • ARM が提供する再入可能バージョンの _rand_r() および _srand_r() を使用する。これらの関数では、C ライブラリ内のスタティックデータではなく、ユーザ指定のバッファが使用されます。
  • 一度に 1 つのスレッドのみが rand() を呼び出すように独自のロックを使用する。例えば、コードを変更せずにロックを適用するには、$Sub$$rand() を定義します。
  • 乱数を生成する必要があるスレッドが 1 つのみになるように調整する。
  • 複数の独立したインスタンスを持つことができる、独自の乱数ジェネレータを用意する。

_rand_r() および _srand_r() はいずれも、追加のパラメータとして、乱数ジェネレータの状態が格納されるバッファを指すポインタを取ります。
setlocale()、localeconv()
setlocale() は、ロケールの設定および読み出しに使用されます。ロケール設定はすべてのスレッドを通じてグローバルであり、ロックによって保護されていません。ロケール設定を変更するために 2 つのスレッドによって setlocale() が呼び出された場合、または、1 つのスレッドが設定を読み出し、別のスレッドが設定を変更した場合、データが破損する可能性があります。また、strtod()sprintf() など、その他の多くの関数も、現在のロケール設定を読み出します。したがって、1 つのスレッドが他のスレッドと同時に setlocale() を呼び出すと、予期しない結果になる場合があります。
複数のスレッドによる設定の同時読み出しは、単純な処理の場合、および、これらの設定を同時に変更するスレッドが存在しない場合はスレッドセーフです。ただし、返される結果が複雑で内部的に中間バッファが必要な場合は、再入可能バージョンの setlocale() を使用しない限り、予期しない結果になる可能性があります。
ARM では以下のいずれかの方法を推奨します。
  • 使用するロケールを 1 つに決定し、setlocale() を一度だけ呼び出して初期化する。これは、プログラムでスレッドを追加する前に行って下さい。これにより、複数のスレッドが相互に干渉することなく同じロケール設定を同時に読み出すことができます。
  • ARM が提供する再入可能バージョンの _setlocale_r() を使用する。この関数は、C ライブラリ内のメモリを使用する代わりに、定数文字列を指すポインタ、または、スレッドローカルストレージとして使用できるユーザ定義のバッファに格納される文字列を指すポインタを返します。バッファの長さは、少なくとも _SETLOCALE_R_BUFSIZE バイトである必要があります(末尾の NUL のスペースを含む)。
_setlocale_r() は、ロケール設定の変更のために同時にアクセスされる場合があり、完全なスレッドセーフではないことに注意して下さい。このようなアクセスが行われた場合、ロックでは保護されません。
また、localeconv() もスレッドセーフではありません。この関数の代わりに、ユーザ指定バッファを指すポインタを使用して、ARM 関数 _get_lconv() を呼び出して下さい。
関連する概念
1.21 ARM C ライブラリでのスレッドセーフティ
関連する参考文書
4.20 _rand_r()
4.33 _srand_r()
a
gamma() ARM® コンパイラ 4.1 以降では廃止される予定であるため、RVCT から移行する場合は注意して下さい。
非機密扱いPDF file icon PDF 版ARM DUI0475KJ
Copyright © 2010-2014 ARM.All rights reserved.