マルチスレッドのプログラミング

第 3 章 スレッド生成時の属性設定

前章では、デフォルト属性を使ったスレッド生成の基礎について説明しました。この章では、スレッド生成時における属性の設定方法を説明します。

なお、pthread だけが属性と取り消しを使用するので、この章で取り上げている API は POSIX スレッドのみに対応します。それ以外は、Solaris スレッドと pthread は機能的にはほぼ同じです(両者の類似点と相違点については、第 8 章「Solaris スレッドを使ったプログラミング」を参照してください。)

属性

属性は、デフォルトとは異なる動作を指定する手段です。pthread_create(3THR) でスレッドを生成する場合、または同期変数を初期化する場合は、属性オブジェクトを指定できます。通常は、デフォルトで間に合います。

属性オブジェクトはプログラマからは「不透明」なため、代入によって直接変更できません。各オブジェクト型を初期化、設定、または削除するための関数のセットが用意されています。

いったん初期化して設定した属性は、プロセス全体に適用されます。属性を使用するための望ましいやり方は、必要なすべての状態の指定をプログラム実行の初期の段階で一度に設定することです。そうすれば、必要に応じて適切な属性オブジェクトを参照できます。

属性オブジェクトを使用することには、主に次の 2 つの利点があります。

属性オブジェクトの取り扱いで注意を要するのは、プロセス終了時です。オブジェクトが初期化されるときにメモリーが割り当てられます。このメモリーをシステムに戻す必要があります。pthread 規格には、属性オブジェクトを削除する関数呼び出しが用意されています。

属性の初期化

pthread_attr_init(3THR)

pthread_attr_init(3THR) は、オブジェクトの属性をデフォルト値に初期化します。その記憶領域は、実行中にスレッドシステムによって割り当てられます。


プロトタイプ:

int pthread_attr_init(pthread_attr_t *tattr);

#include <pthread.h>

pthread_attr_t tattr;
int ret;

/* 属性をデフォルト値に初期化する */
ret = pthread_attr_init(&tattr);

表 3–1 に属性 (tattr) のデフォルト値を示します。

表 3–1 tattr のデフォルト属性値

属性 

値 

結果 

scope

PTHREAD_SCOPE_PROCESS

新しいスレッドは非結合 (LWP に固定的に結合されない) 

detachstate

PTHREAD_CREATE_JOINABLE

スレッドの終了後に終了状態とスレッドが保存される 

stackaddr

NULL

新しいスレッドはシステムによって割り当てられたスタックアドレスをもつ 

stacksize

新しいスレッドはシステムによって定義されたスタックの大きさをもつ 

priority

新しいスレッドは優先順位 0 をもつ 

inheritsched

PTHREAD_EXPLICIT_SCHED

新しいスレッドは親スレッドのスケジューリング優先順位を継承しない 

schedpolicy

SCHED_OTHER

新しいスレッドは、同期オブジェクトの競合が発生した場合に、Solaris が定義した固定優先順位を使用する。 スレッドは、横取りされたり、またはブロックされるか CPU を譲ったときに終了する。 

戻り値

正常終了時は 0 です。それ以外の戻り値は、エラーが発生したことを示します。以下の条件が検出されると、この関数は失敗し、対応する値を返します。


ENOMEM

メモリーが不足し、スレッド属性オブジェクトを初期化できないときに返されます。

属性の削除

pthread_attr_destroy(3THR)

pthread_attr_destroy(3THR) は、初期化時に割り当てられた記憶領域を削除します。その属性オブジェクトは無効になります。


プロトタイプ:
int	pthread_attr_destroy(pthread_attr_t *tattr);

#include <pthread.h>

pthread_attr_t tattr;
int ret;

/* 属性を削除する */
ret = pthread_attr_destroy(&tattr); 

戻り値

正常終了時は 0 です。それ以外の戻り値は、エラーが発生したことを示します。以下の条件が検出されると、この関数は失敗し、対応する値を返します。


EINVAL

tattr の値が無効です。

切り離し状態の設定

pthread_attr_setdetachstate(3THR)

スレッドを切り離された状態 (PTHREAD_CREATE_DETACHED) として生成すると、そのスレッドが終了するとすぐに、そのスレッド識別子とその他のリソースを再利用できます。呼び出したスレッドでスレッドの終了まで待ちたくない場合は、pthread_attr_setdetachstate(3THR) を使用してください。

スレッドを切り離されていない状態 (PTHREAD_CREATE_JOINABLE) として生成すると、そのスレッドを待つものとみなされます。つまり、そのスレッドに対して pthread_join(3T) を実行するとみなされます。

スレッドが切り離された状態で生成されたか切り離されていない状態で生成されたかに関係なく、すべてのスレッドが終了するまでプロセスは終了しません。スレッド終了処理の完了にある、main() から処理途中で戻ることによって生じるプロセスの終了の説明を参照して下さい。


プロトタイプ:

int	pthread_attr_setdetachstate(pthread_attr_t *tattr,int detachstate);

#include <pthread.h>

pthread_attr_t tattr;
int ret;

/* スレッド切り離し状態を設定する */
ret = pthread_attr_setdetachstate(&tattr,PTHREAD_CREATE_DETACHED);

注 –

明示的な同期によって阻止されなければ、そのスレッドの生成元が pthread_create() から復帰する前に、新たに生成される切り離されたスレッドが終了し、そのスレッド識別子が別の新しいスレッドに割り当てられることがあります。


切り離されていない (PTHREAD_CREATE_JOINABLE) スレッドについては、そのスレッドの終了後に他のスレッドが終了待ちを行うことがきわめて重要です。そうしないと、そのスレッドのリソースが新しいスレッドに解放されません。これは通常、メモリーリークを招くことになります。終了待ちを行うつもりがない場合は、スレッド作成時に切り離されたスレッドとして作成してください。


例 3–1 切り離されたスレッドの生成


#include <pthread.h>

pthread_attr_t tattr;
pthread_t tid;
void *start_routine;
void arg
int ret;

/* デフォルト属性で初期化する */
ret = pthread_attr_init(&tattr);
ret = pthread_attr_setdetachstate(&tattr,PTHREAD_CREATE_DETACHED);
ret = pthread_create(&tid, &tattr, start_routine, arg);

戻り値

正常終了時は 0 です。それ以外の戻り値は、エラーが発生したことを示します。以下の条件が検出されると、この関数は失敗し、対応する値を返します。


EINVAL

detachstate または tattr の値が無効です。

切り離し状態の取得

pthread_attr_getdetachstate(3THR)

pthread_attr_getdetachstate(3THR) は、スレッドの生成状態を取得します。これは「切り離された」または「切り離されていない」状態です。


プロトタイプ:

int	pthread_attr_getdetachstate(const pthread_attr_t *tattr,
    int *detachstate;

#include <pthread.h>

pthread_attr_t tattr;
int detachstate;
int ret;

/* スレッドの切り離し状態を取得する */
ret = pthread_attr_getdetachstate (&tattr, &detachstate);

戻り値

正常終了時は 0 です。それ以外の戻り値は、エラーが発生したことを示します。以下の条件が検出されると、この関数は失敗し、対応する値を返します。


EINVAL

detachstate の値が NULL か、tattr の値が無効です。

スタックガードの大きさの設定

pthread_attr_setguardsize(3THR)

pthread_attr_setguardsize(3THR) は、attr オブジェクトの guardsize (ガードサイズ) を設定します。

guardsize 引数は、スタックポインタのオーバーフローを防ぐためのものです。ガードとともにスレッドのスタックが作成されると、実装は、スタックのオーバーフローの終わりに、スタックポインタのスタックオーバーフローの緩衝域として、余分のメモリーを割り当てます。このバッファにアプリケーションがオーバーフローすると、スレッドに SIGSEGV シグナルが配信されるなどのエラーが発生します。

ガードサイズ属性をアプリケーションで使用する目的は、次の 2 つです。

  1. オーバーフローを防止すると、システムリソースが無駄になるおそれがあります。多くのスレッドが作成されるアプリケーションは、そのスレッドがスタックをオーバーフローしないことがわかっている場合には、ガード領域をオフにすることで、システムリソースを節約できます。

  2. スレッドがスタックに割り当てたデータ構造が大きい場合は、スタックオーバーフローを検出するために、大きなガード領域が必要になることがあります。

guardsize が 0 の場合は、attr を使って作成したスレッドにはガード領域が含まれません。guardsize が 0 よりも大きい場合は、少なくとも guardsize バイトのガード領域が、attr を使って作成した各スレッドに割り当てられます。デフォルトでは、スレッドは実装で定義された 1 バイト以上のガード領域を持ちます。

POSIX では、guardsize の値を、設定可能なシステム変数 PAGESIZE (sys/mman.h の「PAGESIZE」を参照) の倍数に切り上げるように、実装が認められています。実装が guardsize の値を PAGESIZE の倍数に切り上げる場合は、attr を指定して pthread_attr_getguardsize() を呼び出すと、guardsize には前回 pthread_attr_setguardsize() を呼び出したときに指定されたガードサイズが使用されます。


#include <pthread.h>

int pthread_attr_setguardsize(pthread_attr_t *attr, size_t  guardsize);

戻り値

以下の戻り値は、pthread_attr_setguardsize() が失敗したことを示します。


EINVAL

引数 attr が無効であるか、引数 guardsize が無効であるか、あるいは guardsize に無効な値が含まれています。

スタックガードの大きさの取得

pthread_attr_getguardsize(3THR)

pthread_attr_getguardsize(3THR) は、attr オブジェクトの guardsize を取得します。

POSIX では、guardsize の値を、設定可能なシステム変数 PAGESIZE (sys/mman.h の「PAGESIZE」を参照) の倍数に切り上げる実装が認められています。実装が guardsize の値を PAGESIZE の倍数に切り上げる場合は、attr を指定して pthread_attr_getguardsize() を呼び出すと、guardsize には前回 pthread_attr_setguardsize() を呼び出したときに指定されたガードサイズが使用されます。


#include <pthread.h>

int pthread_attr_getguardsize(const pthread_attr_t *attr,
                                      size_t  *guardsize);

戻り値

以下の戻り値は、pthread_attr_getguardsize() が失敗したことを示します。


EINVAL

引数 attr が無効であるか、引数 guardsize が無効であるか、あるいは guardsize に無効な値が含まれています。

スコープの設定

pthread_attr_setscope(3THR)

pthread_attr_setscope(3THR) は、結合スレッド (PTHREAD_SCOPE_SYSTEM) または非結合スレッド (PTHREAD_SCOPE_PROCESS) を生成します。


注 –

結合スレッドと非結合スレッドの両方とも、指定されたプロセス内でのみアクセスできます。



プロトタイプ:

int	pthread_attr_setscope(pthread_attr_t *tattr,int scope);

#include <pthread.h>

pthread_attr_t tattr;
int ret;

/* 結合スレッド */
ret = pthread_attr_setscope(&tattr, PTHREAD_SCOPE_SYSTEM);

/* 非結合スレッド */
ret = pthread_attr_setscope(&tattr, PTHREAD_SCOPE_PROCESS);

この例には、次の 3 つの関数呼び出しがあります。属性を初期化するもの、デフォルト属性を変更するもの、および pthread を生成するもののです。


#include <pthread.h>

pthread_attr_t attr;
pthread_t tid;
void start_routine;
void arg;
int ret;

/* デフォルト属性による初期化 */
ret = pthread_attr_init (&tattr);

/* 結合動作 */
ret =  pthread_attr_setscope(&tattr, PTHREAD_SCOPE_SYSTEM);
ret = pthread_create (&tid, &tattr, start_routine, arg);

戻り値

正常終了時は 0 です。それ以外の戻り値は、エラーが発生したことを示します。以下の条件が検出されると、この関数は失敗し、対応する値を返します。


EINVAL

tattr に設定しようとした値は無効です。

スコープの取得

pthread_attr_getscope(3THR)

pthread_attr_getscope(3THR) は、スレッドのスコープを取得します。これはスレッドが結合するかしないかを示します。


プロトタイプ:

int	pthread_attr_getscope(pthread_attr_t *tattr, int *scope);

#include <pthread.h>

pthread_attr_t tattr;
int scope;
int ret;

/* スレッドのスコープを取得する */
ret = pthread_attr_getscope(&tattr, &scope);

戻り値

正常終了時は 0 です。それ以外の戻り値は、エラーが発生したことを示します。以下の条件が検出されると、この関数は失敗し、対応する値を返します。


EINVAL

scope の値が NULL か、tattr の値が無効です。

スレッドの多重度の設定

pthread_setconcurrency(3THR)

pthread_setconcurrency(3THR) は、標準規格に準拠するための属性です。 アプリケーションは、この属性を使用して、スレッドライブラリに目標多重度を通知します。 Solaris 9 に実装されているスレッドでは、このインタフェースは無効です。実行可能スレッドはすべて LWP に接続されます。


#include <pthread.h>

int pthread_setconcurrency(int new_level);

戻り値

以下の戻り値は、pthread_setconcurrency() が失敗したことを示します。


EINVAL

new_level で指定された値が負の値です。


EAGAIN

new_level で指定された値を使用するとシステムリソースの容量を超えます。

スレッド多重度の取得

pthread_getconcurrency(3THR)

pthread_getconcurrency(3THR) は、pthread_setconcurrency() への前回の呼び出しで設定された値を返します。 pthread_setconcurrency() 関数が以前に呼び出されていない場合は、 pthread_getconcurrency() は 0 を返します。


#include <pthread.h>

int pthread_getconcurrency(void);

戻り値

pthread_getconcurrency() は常に、pthread_setconcurrency() の前回の呼び出しで設定された値を返します。pthread_setconcurrency() が呼び出されたことがない場合は、pthread_getconcurrency() は 0 を返します。

スケジューリング方針の設定

pthread_attr_setschedpolicy(3THR)

pthread_attr_setschedpolicy(3THR) は、スケジューリング方針を設定します。POSIX 規格ではスケジューリング方針の属性として、SCHED_FIFO (先入れ先出し)、SCHED_RR (ラウンドロビン)、SCHED_OTHER (実装で定義) を規定しています。

SCHED_FIFOSCHED_RR は POSIX では任意とされており、リアルタイム結合スレッドについてのみサポートされています

スケジューリングの説明については、スケジューリングの節を参照してください。


プロトタイプ:

int	pthread_attr_setschedpolicy(pthread_attr_t *tattr, int policy);

#include <pthread.h>

pthread_attr_t tattr;
int policy;
int ret;

/* スケジューリング方針を SCHED_OTHER に設定する */
ret = pthread_attr_setschedpolicy(&tattr, SCHED_OTHER);

戻り値

正常終了時は 0 です。それ以外の戻り値は、エラーが発生したことを示します。以下の条件が検出されると、この関数は失敗し、次の値を戻します。


EINVAL

tattr に設定しようとした値は無効です。


ENOTSUP

サポートされていない属性値を設定しようとしました。

スケジューリング方針の取得

pthread_attr_getschedpolicy(3THR)

pthread_attr_getschedpolicy(3THR) は、スケジューリング方針を取得します。


プロトタイプ:

int	pthread_attr_getschedpolicy(pthread_attr_t *tattr, int *policy);

#include <pthread.h>

pthread_attr_t tattr;
int policy;
int ret;

/* スレッドのスケジューリング方針を取得する */
ret = pthread_attr_getschedpolicy (&tattr, &policy); 

戻り値

正常終了時は 0 です。それ以外の戻り値は、エラーが発生したことを示します。以下の条件が検出されると、この関数は失敗し、対応する値を返します。


EINVAL

policy の値が NULL か、tattr の値が無効です。

継承スケジューリング方針の設定

pthread_attr_setinheritsched(3THR)

pthread_attr_setinheritsched(3THR) は、継承スケジューリング方針を設定します。

継承 (inherit) 値の PTHREAD_INHERIT_SCHED の意味は、生成スレッドで定義されたスケジューリング方針を使用し、pthread_create() 呼び出しで定義されたスケジューリング方針は無視するということです。PTHREAD_EXPLICIT_SCHED (デフォルト)を使用した場合は、pthread_create() 呼び出しでの属性が使用されます。


プロトタイプ:

int	pthread_attr_setinheritsched(pthread_attr_t *tattr, int inherit);

#include <pthread.h>

pthread_attr_t tattr;
int inherit;
int ret;

/* 現在のスケジューリング方針を使用する */
ret = pthread_attr_setinheritsched(&tattr, PTHREAD_EXPLICIT_SCHED);

戻り値

正常終了時は 0 です。それ以外の戻り値は、エラーが発生したことを示します。以下の条件が検出されると、この関数は失敗し、次の値を戻します。


EINVAL

tattr に設定しようとした値は無効です。


ENOTSUP

サポートされていない属性値を設定しようとしました。

継承スケジューリング方針の取得

pthread_attr_getinheritsched(3THR)

pthread_attr_getinheritsched(3THR) は、pthread_attr_setinheritsched() によって設定された、スケジューリング方針を返します。


プロトタイプ:

int	pthread_attr_getinheritsched(pthread_attr_t *tattr, int *inherit);

#include <pthread.h>

pthread_attr_t tattr;
int inherit;
int ret;

/* 生成スレッドのスケジューリング方針および優先順位を取得する */
ret = pthread_attr_getinheritsched (&tattr, &inherit); 

戻り値

正常終了時は 0 です。それ以外の戻り値は、エラーが発生したことを示します。以下の条件が検出されると、この関数は失敗し、対応する値を返します。


EINVAL

inherit の値が NULL か、tattr の値が無効です。

スケジューリングパラメータの設定

pthread_attr_setschedparam(3THR)

pthread_attr_setschedparam(3THR) は、スケジューリングパラメータを設定します。

スケジューリングパラメータは param 構造体で定義します。ただし、サポートされるのは優先順位だけです。新たに生成されるスレッドは、この方針で動作します。


プロトタイプ:

int	pthread_attr_setschedparam(pthread_attr_t *tattr,
    const struct sched_param *param);

#include <pthread.h>

pthread_attr_t tattr;int newprio;sched_param param;newprio = 30;

/* 優先順位を設定する。それ以外は変更なし */
param.sched_priority = newprio;

/* 新しいスケジューリングパラメータを設定する */
ret = pthread_attr_setschedparam 
(&tattr, &param);

戻り値

正常終了時は 0 です。それ以外の戻り値は、エラーが発生したことを示します。以下の条件が検出されると、この関数は失敗し、対応する値を返します。


EINVAL

param の値が NULL か、tattr の値が無効です。

pthread の優先順位は、2 つの方法で管理できます。子スレッドを生成する前に優先順位属性を設定するか、親スレッドの優先順位を変更してまた戻すことができます。

スケジューリングパラメータの取得

pthread_attr_getschedparam(3THR)

pthread_attr_getschedparam(3THR) は、pthread_attr_setschedparam() によって定義されたスケジューリングパラメータを返します。


プロトタイプ:

int	pthread_attr_getschedparam(pthread_attr_t *tattr,
    const struct sched_param *param);

#include <pthread.h>

pthread_attr_t attr;
struct sched_param param;
int ret;

/* 既存のスケジューリングパラメータを取得する */
ret = pthread_attr_getschedparam (&tattr, &param);

戻り値

正常終了時は 0 です。それ以外の戻り値は、エラーが発生したことを示します。以下の条件が検出されると、この関数は失敗し、対応する値を返します。


EINVAL

param の値が NULL か、tattr の値が無効です。

指定の優先順位をもつスレッドを生成する

スレッドを生成する前に優先順位属性を設定できます。子スレッドは、sched_param 構造体で指定した新しい優先順位で生成されます (この構造体には他のスケジューリング情報も含まれます)。

既存のパラメータを取得し、優先順位を変更してスレッドを作成してから、優先順位を再設定するという方法をお勧めします。

この方法を例 3–2 に示します。


例 3–2 優先順位を設定したスレッドの生成


#include <pthread.h>
#include <sched.h>

pthread_attr_t tattr;pthread_t tid;int ret;int newprio = 20;
sched_param param;

/* デフォルト属性で初期化する */
ret = pthread_attr_init (&tattr);

/* 既存のスケジューリングパラメータを取得する */
ret = pthread_attr_getschedparam (&tattr, &param);/

* 優先順位を設定する。それ以外は変更なし */
param.sched_priority = newprio;

/* 新しいスケジューリングパラメータを設定する */
ret = pthread_attr_setschedparam (&tattr, &param);

/* 指定した新しい優先順位を使用する */
ret = pthread_create (&tid, &tattr, func, arg);

スタックの大きさの設定

pthread_attr_setstacksize(3THR)

pthread_attr_setstacksize(3THR) は、スレッドのスタックの大きさを設定します。

スタックサイズ属性は、システムが割り当てるスタックの大きさ (バイト数) を定義します。この大きさは、システムで定義された最小のスタックの大きさを下回ってはいけません。詳細については、スタックについてを参照してください。


プロトタイプ:

int	pthread_attr_setstacksize(pthread_attr_t *tattr, size_t size);

#include <pthread.h>

pthread_attr_t tattr;size_t size;int ret;

size = (PTHREAD_STACK_MIN + 0x4000);

/* 新しい大きさを設定する */
ret = pthread_attr_setstacksize(&tattr, size);

上の例では、新しいスレッドが使用するスタックのバイト数が size に納められています。size の値が 0 ならば、デフォルトの大きさが使われます。通常は 0 を指定してください。

PTHREAD_STACK_MIN は、スレッドを起動する上で必要なスタック空間の大きさです。しかし、アプリケーションコードを実行するのに必要なスレッドの関数が必要とするスタック空間の大きさは含まれていません。

戻り値

正常終了時は 0 です。それ以外の戻り値は、エラーが発生したことを示します。以下の条件が検出されると、この関数は失敗し、対応する値を返します。


EINVAL

size の値が PTHREAD_STACK_MIN より小さいか、またはシステムの制限を超過しているか、または tattr が無効です。

スタックの大きさの取得

pthread_attr_getstacksize(3THR)

pthread_attr_getstacksize(3THR) は、pthread_attr_setstacksize() によって設定された、スタックの大きさを返します。


プロトタイプ:

int	pthread_attr_getstacksize(pthread_attr_t *tattr, size_t *size);

#include <pthread.h>

pthread_attr_t tattr;size_t size;int ret;

/* スタックの大きさを取得する */
ret = pthread_attr_getstacksize(&tattr, &size);

戻り値

正常終了時は 0 です。それ以外の戻り値は、エラーが発生したことを示します。以下の条件が検出されると、この関数は失敗し、対応する値を返します。


EINVAL

tattr が無効です。

スタックについて

通常、スレッドスタックはページ境界で始まり、指定した大きさは次のページ境界まで切り上げられます。アクセス権のないページがスタックのオーバーフローの最後に付加されることにより、ほとんどのスタックオーバーフローで、違反したスレッドに SIGSEGV シグナルが送られるようになります。呼び出し側によって割り当てられるスレッドスタックは、そのまま使われます。

スタックを指定するときは、スレッドを PTHREAD_CREATE_JOINABLE として生成してください。このスタックは、そのスレッドに対する pthread_join(3THR) 呼び出しが戻るまで解放できません。これは、スレッドのスタックは、そのスレッドが終了するまで解放できないからです。スレッドが終了したかどうかを確実に知るには、pthread_join(3THR) を使用してください。

通常、スレッド用にスタック空間を割り当てる必要はありません。スレッドライブラリは、各スレッドのスタックに対して、1M バイト (32 ビットの場合) または 2M バイト (64 ビットの場合) の仮想メモリを割り当てます。スワップ空間は予約されません(このライブラリは、mmap()MAP_NORESERVE オプションを使って割り当てを行います)。

スレッドライブラリで生成される各スレッドスタックには、レッドゾーンがあります。スレッドライブラリはレッドゾーンとして、スタックオーバーフローを捕捉するためのページをスタックのオーバーフローの最後に付加します。このページは無効で、アクセスされるとメモリーフォルトになります。レッドゾーンは、自動的に割り当てられるすべてのスタックに付加されます。これは、その大きさがアプリケーションで指定されたかデフォルトの大きさかに関係なく行われます。


注 –

実行時のスタック要件は一定ではないので、指定したスタックがライブラリの呼び出しと動的リンクに必要な実行時要件を確実に満足するようにしなければなりません。


スタックとスタックの大きさの一方または両方を指定するのが適正であることはほとんどありません。専門家であっても、適切な大きさを指定したかどうかを判断するのは困難です。これは、ABI 準拠のプログラムでもスタックの大きさを静的に判定できないからです。スタックの大きさは、プログラムが実行される、それぞれの実行環境に左右されます。

独自のスタックを構築する

スレッドスタックの大きさを指定するときは、呼び出される関数に必要な割り当てを計算してください。これには、呼び出し手続きで必要とされる量、局所変数、情報構造体が含まれます。

デフォルトスタックと少し違うスタックが必要になることがあります。たとえば、スレッドでデフォルトスタックサイズを超えるスタック空間が必要になる場合です。また、少し分かりにくいケースですが、デフォルトスタックが大きすぎる場合もあります。何千ものスレッドを生成するとすれば、デフォルトスタックでは合計サイズが数 G バイトにもなるため、仮想メモリが足りず、それだけのスタック空間を扱えないかもしれないからです。

スタックの大きさの上限は明らかであることが多いのですが、下限はどうでしょうか。スタックにプッシュされるスタックフレームを、その局所変数などを含めて、すべて扱えるだけのスタック空間が必要です。

マクロ PTHREAD_STACK_MIN を呼び出すと、スタックの大きさの絶対最小値が得られます。このマクロは、NULL 手続きを実行するスレッドに必要なスタック空間の大きさを戻します。実用的なスレッドに必要なスタック空間はもっと大きいので、スタックの大きさを小さくするときは十分注意してください。


#include <pthread.h>

pthread_attr_t tattr;
pthread_t tid;
int ret;

size_t size = PTHREAD_STACK_MIN + 0x4000;

/* デフォルト属性で初期化する */
ret = pthread_attr_init(&tattr);

/* スタックの大きさも設定する */
ret = pthread_attr_setstacksize(&tattr, size);

/* tattr に大きさのみを指定する */
ret = pthread_create(&tid, &tattr, start_routine, arg);

スタックアドレスの設定

pthread_attr_setstackaddr(3THR)

pthread_attr_setstackaddr(3THR) は、スレッドスタックのアドレスを設定します。

stackaddr 属性は、スレッドのスタックのベースを定義するものです。これを NULL 以外の値に設定すると (NULL がデフォルト)、そのスタックはそのアドレスで初期化されます。


プロトタイプ:

int	pthread_attr_setstackaddr(pthread_attr_t *tattr,void *stackaddr);

#include <pthread.h>

pthread_attr_t tattr;
void *base;
int ret;

base = (void *) malloc(PTHREAD_STACK_MIN + 0x4000);

/* 新しいアドレスを設定する */
ret = pthread_attr_setstackaddr(&tattr, base);

前の例では、新しいスレッドが使用するスタックのアドレスが base に格納されます。base の値が NULL ならば、pthread_create(3THR) によって新しいスレッドに少なくとも PTHREAD_STACK_MIN バイトのスタックが割り当てられます。

戻り値

正常終了時は 0 です。それ以外の戻り値は、エラーが発生したことを示します。以下の条件が検出されると、この関数は失敗し、対応する値を返します。


EINVAL

base または tattr の値が正しくありません。

次の例は、独自のスタックアドレスとサイズを指定してスレッドを生成する方法を示します。


#include <pthread.h>

pthread_attr_t tattr;
pthread_t tid;
int ret;
void *stackbase;

/* デフォルト属性で初期化する */
ret = pthread_attr_init(&tattr);

/* スタックの大きさを設定する */
ret = pthread_attr_setstacksize(&tattr, size);

/* スタックの基底アドレスを設定する */
ret = pthread_attr_setstackaddr(&tattr, stackbase);

/* アドレスと大きさを指定する */
ret = pthread_create(&tid, &tattr, func, arg);

スタックアドレスの取得

pthread_attr_getstackaddr(3THR)

pthread_attr_getstackaddr(3THR) は、pthread_attr_setstackaddr() によって設定された、スレッドスタックのアドレスを返します。


プロトタイプ:

int	pthread_attr_getstackaddr(pthread_attr_t *tattr,void * *stackaddr);

#include <pthread.h>

pthread_attr_t tattr;
void *base;
int ret;

/* 新しいアドレスを取得する */
ret = pthread_attr_getstackaddr (&tattr, &base); 

戻り値

正常終了時は 0 です。それ以外の戻り値は、エラーが発生したことを示します。以下の条件が検出されると、この関数は失敗し、対応する値を返します。


EINVAL

base または tattr の値が正しくありません。