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

Pthreads ライブラリ

Pthreads API ライブラリは、100 を超える関数で構成されています。使用法のカテゴリでグループ化された関数の完全なリストについては、pthreads(5) のマニュアルページを参照してください。

この節では、スレッドの基本的なプログラミングに使用される関数について簡単に説明しています。これらの説明は、各関数が実行するタスクに従って構成され、関連する API 関数のマニュアルページへのリンクが含まれています。次のリストは、特定のタスクが説明されている場所を示しています。

デフォルトのスレッドの生成

属性オブジェクトを指定しなければ NULL となり、下記の属性を持つデフォルトスレッドが生成されます。

pthread_attr_init() でデフォルト属性オブジェクトを生成し、この属性オブジェクトを使ってデフォルトスレッドを生成することもできます。詳細については、「属性の初期化」を参照してください。

pthread_create の構文

現在のプロセスへ新たに制御スレッドを追加するときは、pthread_create(3C) を使用します。

int pthread_create(pthread_t *restrict tid, const pthread_attr_t 
    *restrict tattr, void*(*start_routine)(void *), void *restrict arg);
#include <pthread.h>

pthread_attr_t() tattr;
pthread_t tid;
extern void *start_routine(void *arg);
void *arg;
int ret; 

/* default behavior*/
ret = pthread_create(&tid, NULL, start_routine, arg);

/* initialized with default attributes */
ret = pthread_attr_init(&tattr);
/* default behavior specified*/
ret = pthread_create(&tid, &tattr, start_routine, arg);

pthread_create() 関数は、必要な状態動作を持つ attr で呼び出されます。start_routine は新しいスレッドで実行する関数です。start_routine で指定した関数が終了すると、スレッドはその関数の戻り値を終了状態に設定して終了します。pthread_create の構文」を参照してください。

pthread_create() が正常終了すると、生成されたスレッドの識別子が tid の指す記憶場所に格納されます。

pthread_create() の属性引数 (デフォルト属性) として NULL を指定すると、デフォルトスレッドが生成されます。()tattr は初期化されると、デフォルト動作を獲得します。

pthread_create の戻り値

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


EAGAIN

説明:

システム制限を超えました。たとえば、生成するスレッド数が多すぎます。


EINVAL

説明:

tattr の値が無効です。


EPERM

説明:

呼び出し元は、必要なスケジューリングパラメータまたはスケジューリングポリシーを設定するための適切なアクセス権を持っていません。

スレッドの終了待ち

pthread_join() 関数は、指定したスレッドが終了するまで呼び出しスレッドをブロックします。

pthread_join の構文

スレッドの終了待ちを行うには、pthread_join(3C) を使用します。

int pthread_join(pthread_t tid, void **status);
#include <pthread.h>

pthread_t tid;
int ret;
void *status;

/* waiting to join thread "tid" with status */
ret = pthread_join(tid, &status);

/* waiting to join thread "tid" without status */
ret = pthread_join(tid, NULL); 

指定するスレッドは、現在のプロセス内のスレッドで、しかも切り離されていないものでなければなりません。スレッドの切り離しについては、「切り離し状態の設定」を参照してください。

statusNULL でなければ、pthread_join() の正常終了時に status の指す記憶場所に終了したスレッドの終了状態が格納されます。

複数のスレッドが同じスレッドの終了を待つ場合、すべてのスレッドはそのスレッドが終了するまで待機します。そして、待機しているスレッドの 1 つが成功を返します。待機しているほかのスレッドは ESRCH エラーを返して失敗します。

pthread_join() の復帰後は、終了したスレッドに関連付けられていたデータ領域がそのアプリケーションで再利用できるようになります。

pthread_join の戻り値

pthread_join() は、正常終了時に 0 を返します。それ以外の戻り値は、エラーが発生したことを示します。以下のいずれかの条件が検出されると、pthread_join() は失敗し、次の値を返します。


ESRCH

説明:

指定のスレッド ID に対応するスレッドが見つかりません。


EDEADLK

説明:

デッドロックが発生しています。たとえば、スレッドが自身を待機していたり、スレッド A とスレッド B が互いに待機し合っていたりします。


EINVAL

説明:

指定のスレッド ID に対応するスレッドは、切り離されています。

pthread_join() は、切り離されていないスレッドに対してだけ有効です。終了時のタイミングで特に同期をとる必要がないスレッドは、切り離して生成してください。

簡単なスレッドの例

例 2–1 では、あるスレッドが最上位の手続きを実行し、手続き fetch() を実行する補助スレッドを生成します。fetch() 手続きは複雑なデータベース検索を行うため、処理に多少時間がかかります。

メインスレッドでは検索結果も必要ですが、その間に行うべき処理があります。そこで、こうした必要な処理を行なってから、pthread_join() で補助スレッドの終了を待ちます。

新しいスレッドへの引数 pbe がスタックパラメータとして渡されます。これが可能なのは、メインスレッドが自分の子スレッドの終了を待つからです。しかし、通常は、malloc でヒープから領域を割り当てる方法をお勧めします。スレッドのスタック領域にアドレスを渡す方法だと、スレッドが終了したときアドレスが無効になったり、割り当て直されたりする可能性があります。


例 2–1 簡単なスレッドプログラム

void mainline (...)
{
        struct phonebookentry *pbe;
        pthread_attr_t tattr;
        pthread_t helper;
        void *status;

        pthread_create(&helper, NULL, fetch, &pbe);

            /* do something else for a while */

        pthread_join(helper, &status);
        /* it's now safe to use result */
}

void *fetch(struct phonebookentry *arg)
{
        struct phonebookentry *npbe;
        /* fetch value from a database */

        npbe = search (prog_name)
            if (npbe != NULL)
                *arg = *npbe;
        pthread_exit(0);
}   

struct phonebookentry {
        char name[64];
        char phonenumber[32];
        char flags[16];
}

スレッドの切り離し

pthread_detach(3C) は、detachstate 属性を PTHREAD_CREATE_JOINABLE に設定して生成されたスレッドの記憶領域を再利用するため、pthread_join(3C) の代わりに利用できます。

pthread_detach の構文

int pthread_detach(pthread_t tid);
#include <pthread.h>

pthread_t tid;
int ret;

/* detach thread tid */
ret = pthread_detach(tid); 

pthread_detach() 関数は、スレッド tid のための記憶領域がそのスレッドの終了時に再利用できることを、アプリケーションに対して示すために使われます。スレッドは、必要なくなった時点で切り離すべきです。tid が終了していない場合、pthread_detach() によって、そのスレッドが終了することはありません。

pthread_detach の戻り値

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


EINVAL

説明:

tid は切り離されたスレッドです。


ESRCH

説明:

tid は、現在のプロセスの中の有効な切り離されていないスレッドではありません。

スレッド固有データキーの作成

シングルスレッドの C プログラムは、2 つの基本クラスデータを持っています。 局所データと大域データです。マルチスレッドの C プログラムには、さらに第 3 のクラスであるスレッド固有データ (TSD) が追加されます。スレッド固有データは大域データと似ていますが、スレッドごとの専用のデータである点が異なります。


注 –

Solaris OS では、スレッドが大域変数のプライベートコピーを持つことができる代替機能がサポートされています。この機構は、スレッドローカルな記憶領域 (TLS) と呼ばれます。スレッドローカルにする変数を宣言するためにキーワード __thread が使用され、コンパイラは、これらの変数がスレッド単位に割り当てられるように自動的に配置します。詳細については、『リンカーとライブラリ』の第 8 章「スレッド固有領域 (TLS)」を参照してください。


スレッド固有データ (TSD) は、スレッド単位で維持管理されます。TSD は、特定のスレッド固有のデータを定義し参照する唯一の手段となります。スレッド固有データの各項目は、プロセス内のすべてのスレッドから参照可能な特定のキー (key) と関連付けられます。そのキー (key) を使用することによって、スレッドはスレッド単位で維持管理されるポインタ (void *) にアクセスできます。

pthread_key_create の構文

int pthread_key_create(pthread_key_t *key,
    void (*destructor) (void *));
#include <pthread.h>

pthread_key_t key;
int ret;

/* key create without destructor */
ret = pthread_key_create(&key, NULL);

/* key create with destructor */
ret = pthread_key_create(&key, destructor); 

pthread_key_create(3C) は、プロセス内のスレッド固有データを識別するためのキー (key) を割り当てます。このキーは、プロセス内のすべてのスレッドに大域的なキーです。スレッド固有データが生成された時点では、すべてのスレッドで、キーの初期値として NULL が関連付けられています。

pthread_key_create() は、キーの使用前に各キーについて 1 回呼び出します。プロセス内のすべてのスレッドが共有するキーについては、暗黙の同期はありません。

作成されたキーに対して、各スレッドは特定の値を結び付けることができます。その値はスレッドに固有で、スレッドごとに独立に維持管理されます。スレッド単位での割り当ては、キーがデストラクタ関数 (destructor) で作成された場合は、スレッドの終了時にその割り当てを解除されます。

pthread_key_create() が正常終了すると、割り当てられたキーは key が指す位置に格納されます。このキーに対する記憶領域とアクセスとの同期は呼び出し側の責任でとらなければなりません。

各キーに任意で、デストラクタ関数を関連付けることができます。あるキーが NULL でないデストラクタ関数を持っていて、スレッドがそのキーに対して NULL 以外の値を関連付けている場合、そのスレッドの終了時に現在関連付けられている値を指定してデストラクタ関数が呼び出されます。どの順番でデストラクタ関数が呼び出されるかは不定です。

pthread_key_create の戻り値

pthread_key_create() は、正常終了時に 0 を返します。それ以外の戻り値は、エラーが発生したことを示します。以下のいずれかの条件が検出されると pthread_key_create() は失敗し、次の値を戻します。


EAGAIN

説明:

キーの名前空間が使い果たされました。


ENOMEM

説明:

仮想記憶が足りないので、新しいキーを作成できません。

スレッド固有データキーの削除

既存のスレッド固有データキーを削除するには、pthread_key_delete(3C) を使用します。このキーは無効になっているので、キーに関連付けられているあらゆるメモリーを解放できます。無効なキーが参照されると、エラーが返されます。

pthread_key_delete の構文

int pthread_key_delete(pthread_key_t key);
#include <pthread.h>

pthread_key_t key;
int ret;

/* key previously created */
ret = pthread_key_delete(key); 

key を削除したあとで pthread_setspecific() または pthread_getspecific () 呼び出しを使用してそのキーを参照しようとすると、予期しない結果が発生します。

pthread_key_delete() 関数を呼び出す前にスレッド固有のリソースを解放するのは、プログラマの責任です。この関数はデストラクタ関数をいっさい呼び出しません。pthread_key_create()pthread_key_delete() を繰り返し呼び出すと、問題が発生する可能性があります。

この問題は、Solaris 実装で、key の値が pthread_key_delete() によって無効にされたあとで再利用されないために発生します。pthread_key_create() を呼び出すたびに新しいキー値が割り当てられるため、キー情報を保持するために割り当てられる内部メモリーが増えます。pthread_key_create() ... pthread_key_delete() の無限ループによって、最終的にはすべてのメモリーが使い果たされます。可能であれば、目的の各キーに対して pthread_key_create() を 1 回だけ呼び出し、pthread_key_delete() は呼び出さないようにしてください。

pthread_key_delete の戻り値

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


EINVAL

説明:

key の値が有効ではありません。

スレッド固有データの設定

指定したスレッド固有データキーに対して、スレッド固有の割り当てを設定するには、pthread_setspecific(3C) を使用します。

pthread_setspecific の構文

int pthread_setspecific(pthread_key_t key, const void *value);
#include <pthread.h>

pthread_key_t key;
void *value;
int ret;

/* key previously created */
ret = pthread_setspecific(key, value); 

pthread_setspecific の戻り値

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


ENOMEM

説明:

仮想メモリーが不足しています。


EINVAL

説明:

キーが無効です。


注 –

pthread_setspecific() は、新しい割り当てが設定されても、記憶領域を解放しません。既存の割り当ては、解放する必要があります。解放しない場合は、メモリーリークが発生することがあります。


スレッド固有データの取得

key の呼び出しスレッドの割り当てを取得し、この割り当て情報を value がポイントする記憶場所に格納するには、pthread_getspecific(3C) を使用します。

pthread_getspecific の構文

void *pthread_getspecific(pthread_key_t key);
#include <pthread.h>

pthread_key_t key;
void *value;

/* key previously created */
value = pthread_getspecific(key); 

pthread_getspecific の戻り値

pthread_getspecific はエラーを返しません。

スレッド固有データの大域性と局所性の例

例 2–2 は、あるマルチスレッドプログラムの抜粋です。このコードは任意の数のスレッドによって実行されますが、コードでは 2 つの大域変数 errnomywindow を参照しています。これらの大域値は、実際には各スレッド専用の項目への参照になります。


例 2–2 スレッド固有データの大域性と局所性

body() {
    ...

    while (write(fd, buffer, size) == -1) {
        if (errno != EINTR) {
            fprintf(mywindow, "%s\n", strerror(errno));
            exit(1);
        }
    }

    ...

}

errno を参照すれば、そのスレッドが呼び出したルーチンから戻されたシステムエラーコードがわかります。ほかのスレッドが呼び出したシステムコールではありません。ヘッダーファイル errno.h をインクルードすると、errno の参照が errno のスレッド専用インスタンスの参照になります。これにより、あるスレッドによる errno の参照が、ほかのスレッドによる errno の参照とは別の記憶領域の場所を参照するようになります。

変数 mywindow は、それを参照するスレッドの専用のウィンドウに接続される stdio ストリームを参照するための変数です。したがって、errno と同様に、あるスレッドによる mywindow の参照は、ほかのスレッドによる mywindow の参照とは別の記憶領域を参照します。最終的には、この参照は異なるウィンドウへの参照になります。ここでの唯一の違いは、errno はシステムによって処理されるのに対し、mywindow はプログラマが処理しなければならない点です。

次の例は、mywindow の参照がどのように働くかを示しています。プリプロセッサは、mywindow の参照を _mywindow() 手続きの呼び出しに変換します。

このルーチンは、 pthread_getspecific() を呼び出し、pthread_getspecific() に、大域変数 mywindow_key と、このスレッドのウィンドウの識別情報が入る出力パラメータ win を渡します。


例 2–3 大域参照から局所参照への変換

thread_key_t mywin_key; 

FILE *_mywindow(void) {
 FILE *win;
 win = pthread_getspecific(mywin_key);
 return(win);
 }
#define mywindow _mywindow()

void routine_uses_win( FILE *win) {
 ... 
} 
void thread_start(...) {
 ... 
 make_mywin();
 ... 
 routine_uses_win( mywindow )
 ... 
}

変数 mywin_key は、スレッドごとに実体を持つことができる変数のまとまりを識別します。つまり、これらの変数はスレッド固有データです。各スレッドは make_mywin() を呼び出して自分専用のウィンドウを初期化し、スレッド固有データの参照用に自分専用の mywindow のインスタンスを配置します。

make_mywin を呼び出したスレッドは、mywindow を安全に参照できるようになり、さらに _mywindow() の実行後は、自分専用のウィンドウを参照できるようになります。結果的に、mywindow の参照は、そのスレッドの専用のデータの直接の参照であるかのように見えます。

例 2–4 は、参照の設定方法を示しています。


例 2–4 スレッド固有データの初期化

void make_mywindow(void) {
    FILE **win;
    static pthread_once_t mykeycreated = PTHREAD_ONCE_INIT;

    pthread_once(&mykeycreated, mykeycreate);

    win = malloc(sizeof(*win));
    create_window(win, ...);

    pthread_setspecific(mywindow_key, win);
}

void mykeycreate(void) {
    pthread_key_create(&mywindow_key, free_key);
}

void free_key(void *win) {
    free(win);
}

まず最初に、mywin_key キーに一意的な値を取得します。これはスレッド固有データのクラスを識別するために使用するキーです。具体的には、make_mywin() を呼び出す最初のスレッドが pthread_key_create() を呼び出します。その結果、この関数の第 1 引数に一意なキーが割り当てられます。第 2 引数はデストラクタ関数で、このスレッド固有データ項目のスレッド専用インスタンスをスレッドの終了時に解放するためのものです。

次に、呼び出し側の、このスレッド固有データ項目のインスタンスのために記憶領域を確保します。その後、create_window() を呼び出して、スレッド用にウィンドウを設定します。win は、ウィンドウに割り当てられた記憶領域をポイントします。最後に pthread_setspecific() が呼び出され、win とキーとが結び付けられます。

続いて、スレッドは pthread_getspecific() を呼び出して、上記の大域キー (key) を渡します。その結果、スレッドは、以前の pthread_setspecific() 呼び出しでこのキーに関連付けた値を取得できます。

スレッドが終了するときは、pthread_key_create() で設定したデストラクタ関数が呼び出されます。各デストラクタ関数は、そのスレッドが pthread_setspecific() でキーに値を設定している場合だけ呼び出されます。

スレッド識別子の取得

呼び出しスレッドのスレッド識別子 (thread identifier) を取得するには、 pthread_self(3C) を使用します。

pthread_self の構文

pthread_t  pthread_self(void);
#include <pthread.h>

pthread_t tid;

tid = pthread_self();

pthread_self の戻り値

pthread_self() は、呼び出しスレッドのスレッド識別子 (thread identifier) を返します。

スレッド識別子の比較

2 つのスレッドのスレッド識別番号を比較するには、pthread_equal(3C) を使用します。

pthread_equal の構文

int  pthread_equal(pthread_t tid1, pthread_t tid2);
#include <pthread.h>

pthread_t tid1, tid2;
int ret;

ret = pthread_equal(tid1, tid2);

pthread_equal の戻り値

pthread_equal() は、tid1tid2 が等しいときは 0 以外の値、それ以外の場合は 0 を返します。tid1 または tid2 が無効なスレッド識別番号の場合は、結果は予測できません。

スレッドの初期化ルーチンの呼び出し

スレッド化されたプロセス内の pthread_once(3C) は、最初に呼び出されたとき、初期化ルーチンを呼び出します。 プロセス内の任意のスレッドからの 2 回目以降の pthread_once() の呼び出しは何の効果もありません。

pthread_once の構文

int  pthread_once(pthread_once_t *once_control, void (*init_routine)(void));
#include <pthread.h>

pthread_once_t once_control = PTHREAD_ONCE_INIT;
int ret;

ret = pthread_once(&once_control, 
init_routine);

once_control パラメータは、該当する初期化ルーチンがすでに呼び出されているかどうかを判定します。

pthread_once の戻り値

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


EINVAL

説明:

once_control または init_routineNULL です。

スレッドの実行明け渡し

sched_yield(3RT) は、現在のスレッドから同じ優先順位か、より高い優先順位をもつ別のスレッドに実行権を譲ります。すぐに実行可能なこのようなスレッドが存在しない場合、呼び出しスレッドは実行を継続します。sched_yield() 関数は Pthread API の一部ではなく、リアルタイムライブラリ関数に含まれている関数です。sched_yield() を使用するには、<sched.h> をインクルードする必要があります。

sched_yield の構文

int  sched_yield(void);
#include <sched.h>
int ret;
ret = sched_yield();

sched_yield の戻り値

sched_yield() は、正常終了時に 0 を返します。そうでなければ -1 が戻され、errno にエラー条件が設定されます。

スレッドの方針およびスケジューリングパラメータの設定

個々のスレッドのスケジューリングポリシーおよびスケジューリングパラメータを変更するには、pthread_setschedparam(3C) を使用します。

pthread_setschedparam の構文

int pthread_setschedparam(pthread_t tid, int  policy,
    const struct sched_param *param);
#include <pthread.h>

pthread_t tid;
int ret;
struct sched_param param;
int priority;

/* sched_priority will be the priority of the thread */
sched_param.sched_priority = priority;
policy = SCHED_OTHER;

/* scheduling parameters of target thread */
ret = pthread_setschedparam(tid, 
policy, &param); 

サポートされている方針は、SCHED_FIFOSCHED_RR、および SCHED_OTHER です。

pthread_setschedparam の戻り値

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


EINVAL

説明:

設定しようとした属性の値が無効です。


EPERM

説明:

呼び出し元は、指定されたスレッドのスケジューリングパラメータまたはスケジューリングポリシーのいずれかを設定するための適切なアクセス権を持っていません。


ESRCH

説明:

tid で指定した値が既存のスレッドを表していません。

スレッドの方針およびスケジューリングパラメータの取得

pthread_getschedparam(3C) は、個々のスレッドのスケジューリングポリシーおよびスケジューリングパラメータを取得します。

pthread_getschedparam の構文

int  pthread_getschedparam(pthread_t tid, int *restrict policy,
    struct sched_param *restrict param);
#include <pthread.h>

pthread_t tid;
sched_param param;
int priority;
int policy;
int ret;

/* scheduling parameters of target thread */
ret = pthread_getschedparam (tid, &policy, &param);

/* sched_priority contains the priority of the thread */
priority = param.sched_priority; 

pthread_getschedparam の戻り値

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


ESRCH

説明:

tid で指定した値が既存のスレッドを表していません。

スレッド優先順位の設定

pthread_setschedprio(3C) は、指定されたスレッドのスケジューリング優先順位を設定します。

pthread_setschedprio の構文

int pthread_setschedprio(pthread_t tid, int prio);
#include <pthread.h>

pthread_t tid;
int prio;
int ret;

pthread_setschedprio の戻り値

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


EINVAL

説明:

prio の値は、指定されたスレッドのスケジューリングポリシーに対しては無効です。


ENOTSUP

説明:

サポートされていない優先順位の値を設定しようとしました。


EPERM

説明:

呼び出し元は、指定されたスレッドのスケジューリング優先順位を設定するための適切なアクセス権を持っていません。


ESRCH

説明:

tid で指定した値が既存のスレッドを表していません。

スレッドへのシグナルの送信

スレッドにシグナルを送信するには、pthread_kill(3C) を使用します。

pthread_kill の構文

int  pthread_kill(pthread_t tid, int sig);
#include <pthread.h>
#include <signal.h>

int sig;
pthread_t tid;
int ret;

ret = pthread_kill(tid, 
sig);

tid で指定したスレッドに sig で指定したシグナルを送ります。()tid は、呼び出しスレッドと同じプロセス内のスレッドでなければなりません。引数 sig は、signal.h(3HEAD) のリスト中の値でなければなりません。

sig が 0 のときはエラーチェックだけが行われ、シグナルは実際には送られません。このエラーチェックは、 tid の妥当性を検査する目的で使用できます。

pthread_kill の戻り値

pthread_kill() は、正常終了時に 0 を返します。それ以外の戻り値は、エラーが発生したことを示します。以下のいずれかの条件が検出されると pthread_kill() は失敗し、次の値が戻されます。


EINVAL

説明:

sig は正しいシグナル番号ではありません。


ESRCH

説明:

現在のプロセスに tid が存在しません。

呼び出しスレッドのシグナルマスクのアクセス

呼び出しスレッドのシグナルマスクを変更したり、検査したりするには、 pthread_sigmask(3C) を使用します。

pthread_sigmask の構文

int pthread_sigmask(int how, const sigset_t *new, sigset_t *old);
#include <pthread.h>
#include <signal.h>

int ret;
sigset_t old, new;

ret = pthread_sigmask(SIG_SETMASK, &new, &old); /* set new mask */
ret = pthread_sigmask(SIG_BLOCK, &new, &old); /* blocking mask */
ret = pthread_sigmask(SIG_UNBLOCK, &new, &old); /* unblocking */

引数 how は、シグナルマスクの変更方法を指定します。以下のいずれかの値を指定できます。

new の値が NULL の場合、how の値は重要ではありません。スレッドのシグナルマスクは変更されません。現在ブロックされているシグナルを照会するときは、引数 new の値に NULL を指定してください。

old の指定が NULL でなければ、old の指すアドレスに変更前のシグナルマスクが格納されます。

pthread_sigmask の戻り値

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


EINVAL

説明:

how の値が定義されておらず、oldNULL です。

安全な fork

「解決策: pthread_atfork」pthread_atfork(3C) の説明を参照してください。

pthread_atfork の構文

int pthread_atfork(void (*prepare) (void), void (*parent) (void),
    void (*child) (void) );

pthread_atfork の戻り値

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


ENOMEM

説明:

テーブル空間が足りないので、fork ハンドラのアドレスを記録できません。

スレッドの終了

スレッドを終了するには、pthread_exit(3C) を使用します。

pthread_exit Syntax

void	 pthread_exit(void *status);
#include <pthread.h>
void *status;
pthread_exit(status); /* exit with status */

pthread_exit() は呼び出しスレッドを終了させます。スレッド固有に割り当てられているデータもすべて解放されます。スレッドが切り離されていない場合は、そのスレッド識別子と status によって示される終了状態が保持されます。これらのデータは、アプリケーションがそのスレッドを待機する pthread_join() を呼び出した時点で解放されます。スレッドが切り離されている場合は、status は無視されます。そのスレッド識別子はただちに再利用されます。スレッドの切り離しについては、「切り離し状態の設定」を参照してください。

pthread_exit の戻り値

呼び出しスレッドは、終了状態 status で終了します。

スレッド終了処理の完了

スレッドの終了には下記の方法があります。

デフォルトでは、ほかのスレッドが当該スレッドに対して「終了待ち」を行い、その消滅を確認するまでの間、スレッドは残存します。これはデフォルトの pthread_create() 生成属性の「切り離されていない」と同じです。詳細は、pthread_detach のマニュアルページを参照してください。「終了待ち」操作が行われると当該スレッドの終了状態が取得され、その後、当該スレッドが消滅します。

特に注意すべき特別な場合があります。メインスレッド (すなわち、main() を呼んでいるもの) が main() 呼び出しから戻るか、exit() を呼び出す場合です。この操作が行われるとプロセス全体が終了し、プロセス内のスレッドもすべて終了してしまいます。このため、メインスレッドが main() から処理途中で終了することがないよう十分注意しなければなりません。

メインスレッドが単に pthread_exit を呼び出した場合は、メインスレッドが終了するだけです。プロセス内のその他のスレッドとプロセスは、その後も存続します。すべてのスレッドが終了するとプロセスは終了します。

スレッドの取り消し

スレッド取り消しによって、スレッドはそのプロセス中のほかのスレッドの実行終了を要求することができます。関連のある一群のスレッドの以降の操作がすべて有害または不必要な状況では、取り消しは 1 つの有効な方法です。

スレッドの取り消しの例としては、非同期的に生成される取り消し条件、たとえば実行中のアプリケーションを閉じるまたは終了するというユーザーの要求などがあります。また、複数のスレッドが関わっているタスクの完了などもあります。スレッドの 1 つが最終的にタスクを完了させたのに、ほかのスレッドが動作し続ける場合があります。実行中のスレッドはその時点で何の役にも立っていないため、これらのスレッドは取り消されるべきです。

取り消しポイント

スレッドの取り消しは、取り消しが安全な場合にだけ行なってください。pthread 規格では、下記のような取り消しポイントが規定されています。

デフォルトでは、取り消しが有効 (使用可能) です。場合によっては、アプリケーションで取り消しを無効 (使用不可) にすることがあるかもしれません。取り消しを無効にすると、再度取り消し要求を有効にするまでの間、すべての取り消し要求が保留されます。

取り消しを無効にする方法については、pthread_setcancelstate の構文」を参照してください。

取り消しポイントの配置

取り消しには危険が伴います。そのほとんどは、不変式の復元と共有リソースの解放処理に関係します。不注意に取り消されたスレッドは mutex をロック状態のままにすることがあり、その場合はデッドロックを引き起こします。あるいは、どこか特定できないメモリー領域が割り当てられたままになり、メモリーを解放できなくなります。

標準 C ライブラリでは、取り消しをプログラムにより許可したり禁止したりする取り消しインタフェースを規定しています。どの点で取り消しが可能かを示す一群のポイント (取り消しポイント) も定義されています。このライブラリを使用することにより、取り消しハンドラの有効範囲を定義して、意図した時点で、意図した場所に確実に作用するように設定できます。取り消しハンドラは、リソースと状態を起点と同じ条件に戻すクリーンアップサービスを提供します。

取り消しポイントの配置と取り消しハンドラの効果は、アプリケーションに対する理解に基づくものでなければなりません。mutex は明らかに取り消しポイントではないので、ロックしている時間は必要最小限に留めるべきです。

非同期取り消しの領域は、宙に浮いたリソースや未解決の状態を生じさせるような外部に依存しないシーケンスに限定してください。入れ子の代替取り消し状態から復帰するときは、取り消し状態を復元するように注意してください。このインタフェースは、復元を容易に行えるように次の機能を提供しています。 pthread_setcancelstate(3C) は、参照される変数の中に現在の取り消し状態を保存します。pthread_setcanceltype(3C) は、同様に、現在の取り消しタイプを保存します。

取り消しが起こりうる状況は、次の 3 とおりです。

デフォルトでは、取り消しが起こりうるのは POSIX 規格で定義されているような、明確に定義されたポイントに限られます。

いずれの場合も、リソースと状態が起点と矛盾しない状態に復元されるように注意してください。

スレッドの取り消し

スレッドを取り消すには、pthread_cancel(3C) を使用します。

pthread_cancel の構文

int pthread_cancel(pthread_t thread);
#include <pthread.h>

pthread_t thread;
int ret;

ret = pthread_cancel(thread);

取り消し要求がどのように扱われるかは、対象となるスレッドの状態によって異なります。その状態を判定する関数として、pthread_setcancelstate(3C)pthread_setcanceltype(3C) があります。

pthread_cancel の戻り値

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


ESRCH

説明:

指定されたスレッド ID に対応するスレッドが見つかりません。

取り消しを有効または無効にする

スレッドの取り消しを有効または無効にするには、pthread_setcancelstate(3C) を使用します。スレッドが生成されると、デフォルトでは取り消し機能が有効になります。

pthread_setcancelstate の構文

int pthread_setcancelstate(int state, int *oldstate);
#include <pthread.h>

int oldstate;
int ret;

/* enabled */
ret = pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldstate);

/* disabled */
ret = pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldstate);

pthread_setcancelstate の戻り値

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


EINVAL

説明:

状態が PTHREAD_CANCEL_ENABLE でも PTHREAD_CANCEL_DISABLE でもありません。

取り消しタイプの設定

取り消しタイプを遅延モードまたは非同期モードに設定するには、pthread_setcanceltype(3C) を使用します。

pthread_setcanceltype の構文

int pthread_setcanceltype(int type, int *oldtype);
#include <pthread.h>

int oldtype;
int ret;

/* deferred mode */
ret = pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, &oldtype);

/* async mode*/
ret = pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype);

スレッドが生成されると、デフォルトでは取り消しタイプが遅延モードに設定されます。遅延モードにあるスレッドは、取り消しポイント以外では取り消すことができません。非同期モードにあるスレッドは、実行中の任意のポイントで取り消すことができます。非同期モードを使用するのは好ましくありません。

pthread_setcanceltype の戻り値

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


EINVAL

説明:

PTHREAD_CANCEL_DEFERRED または PTHREAD_CANCEL_ASYNCHRONOUS タイプではありません。

取り消しポイントの設定

スレッドの取り消しポイントを設定するには、pthread_testcancel(3C) を使用します。

pthread_testcancel の構文

void pthread_testcancel(void);
#include <pthread.h>

pthread_testcancel(); 

pthread_testcancel() 関数が実際に機能するのは、取り消し機能が有効にされていて、しかも遅延モードになっているときです。取り消し機能が無効になっている状態で、この関数を呼び出しても何の効果もありません。()

pthread_testcancel() を挿入するのは、スレッドを取り消しても安全なシーケンスに限定してください。pthread_testcancel() 呼び出しを通してプログラムで設定される取り消しポイントの他にも、pthread 規格では、いくつかの取り消しポイントが規定されています。詳細については、「取り消しポイント」を参照してください。

pthread_testcancel の戻り値

pthread_testcancel() は値を返しません。

スタックへハンドラをプッシュする

クリーンアップハンドラは、諸条件を起点のものと矛盾しない状態に復元するために使用します。矛盾しない状態には、割り当てられたリソースのクリーンアップや不変式の復元などが含まれます。ハンドラの管理には、pthread_cleanup_push(3C) 関数と pthread_cleanup_pop(3C) 関数を使用します。

クリーンアップハンドラは、プログラムの同じ字句解析上の範囲でプッシュされてポップされます。プッシュとポップは、常に対になっていなければなりません。そうでないと、コンパイルエラーになります。

pthread_cleanup_push の構文

クリーンアップハンドラをクリーンアップスタック (LIFO) にプッシュするには、pthread_cleanup_push(3C) を使用します。

void pthread_cleanup_push(void(*routine)(void *), void *args);
#include <pthread.h>

/* push the handler "routine" on cleanup stack */
pthread_cleanup_push (routine, arg); 

pthread_cleanup_push の戻り値

pthread_cleanup_push() は値を返しません。

スタックからハンドラを取り出す

クリーンアップスタックからクリーンアップハンドラを取り出すには、pthread_cleanup_pop(3C) を使用します。

pthread_cleanup_pop の構文

void pthread_cleanup_pop(int execute);
#include <pthread.h>

/* pop the "func" out of cleanup stack and execute "func" */
pthread_cleanup_pop (1);

/* pop the "func" and DONT execute "func" */
pthread_cleanup_pop (0); 

この関数への引数が 0 以外なら、指定のハンドラがスタックから取り除かれて実行されます。引数が 0 の場合は、ハンドラはポップされるだけで実行されません。

0 以外の引数を指定して pthread_cleanup_pop() を有効に呼び出せるのは、スレッドが pthread_exit() を明示的または暗黙的に呼び出した場合か、取り消し要求を受け付けた場合です。

pthread_cleanup_pop の戻り値

pthread_cleanup_pop() は値を返しません。