【C言語】 スレッドの終了処理をもっとスマートに! `thrd_exit` 関数とその他の方法を比較


thrd_exit 関数の詳細

thrd_exit 関数は、以下の引数を受け取ります。

  • res: スレッドの終了ステータスを表す整数値

この関数は、以下の処理を行います。

  1. スレッド固有ストレージの解放: スレッド固有ストレージ (TSS) に登録されたすべてのキーについて、関連付けられたデストラクタを呼び出し、値を NULL に設定します。
  2. スレッドの終了: スレッドの実行を停止し、システムリソースを解放します。
  3. 終了ステータスの設定: 終了ステータス res をスレッドに設定します。

thrd_exit 関数の使用例

#include <threads.h>

void *thread_function(void *arg) {
  // スレッド内の処理

  thrd_exit(0); // 正常終了
}

int main() {
  thrd_t tid;

  thrd_create(&tid, thread_function, NULL);

  thrd_join(tid, NULL);

  return 0;
}

この例では、thread_function というスレッド関数が作成され、その中で thrd_exit(0) を呼び出して正常に終了しています。

  • スレッドが終了する前に、適切な後処理を行う必要があります。
  • thrd_exit 関数は、呼び出し側スレッドのみを終了させます。他のスレッドの影響には及びません。


スレッド内の処理完了後に終了する例

#include <threads.h>

void *thread_function(void *arg) {
  // スレッド内の処理

  // 処理完了後に終了
  thrd_exit(0);
}

int main() {
  thrd_t tid;

  thrd_create(&tid, thread_function, NULL);

  thrd_join(tid, NULL);

  return 0;
}

終了ステータスを設定して終了する例

#include <threads.h>

void *thread_function(void *arg) {
  // スレッド内の処理

  // 終了ステータスを設定
  thrd_exit(1); // エラー終了
}

int main() {
  thrd_t tid;
  int status;

  thrd_create(&tid, thread_function, NULL);

  thrd_join(tid, &status);

  if (status == 0) {
    printf("スレッドは正常に終了しました\n");
  } else {
    printf("スレッドはエラーで終了しました\n");
  }

  return 0;
}

この例では、thread_function というスレッド関数が作成され、その中で処理を実行した後、thrd_exit(1) を呼び出してエラー終了しています。main 関数では、thrd_join 関数を使用してスレッドの終了ステータスを取得し、正常終了かエラー終了かを判断しています。

スレッド固有ストレージのデストラクタを呼び出す例

#include <threads.h>

typedef struct {
  int value;
  thrd_destructor_t destructor;
} thread_data_t;

void destructor(void *arg) {
  thread_data_t *data = (thread_data_t *)arg;
  printf("スレッド固有ストレージの値: %d\n", data->value);

  free(data);
}

void *thread_function(void *arg) {
  thread_data_t *data = (thread_data_t *)arg;

  // スレッド内の処理

  // スレッド固有ストレージの解放
  thrd_exit(0);
}

int main() {
  thrd_t tid;
  thread_data_t data = {10, destructor};

  thrd_create(&tid, thread_function, &data);

  thrd_join(tid, NULL);

  return 0;
}

この例では、thread_data_t という構造体を定義し、value フィールドと destructor フィールドをメンバ変数として持たせています。destructor フィールドは、スレッド固有ストレージの解放時に呼び出されるデストラクタ関数へのポインタを格納します。

thread_function 関数では、thread_data_t 構造体のインスタンスをスレッド固有ストレージに登録し、処理を実行した後、thrd_exit(0) を呼び出して終了します。

main 関数では、thread_data_t 構造体のインスタンスを作成し、thrd_create 関数を使用してスレッドを作成します。その後、thrd_join 関数を使用してスレッドの終了を待機します。

スレッドの終了を待つ例

#include <threads.h>

void *thread_function(void *arg) {
  // スレッド内の処理

  // 終了処理

  thrd_exit(0);
}

int main() {
  thrd_t tid;

  thrd_create(&tid, thread_function, NULL);

  // スレッドの終了を待つ
  thrd_join(tid, NULL);

  return 0;
}

この例では、thread_function というスレッド関数が作成され、その中で処理を実行した後、thrd_exit(0) を呼び出して終了しています。main 関数では、thrd_join 関数を使用してスレッドの終了を待機しています。



pthread_exit 関数

pthread_exit 関数は、POSIX Threads 以外のスレッドライブラリで使用される関数であり、thrd_exit 関数と同様の機能を提供します。使用するスレッドライブラリに応じて、適切な関数を選択する必要があります。

#include <pthread.h>

void *thread_function(void *arg) {
  // スレッド内の処理

  // 終了処理

  pthread_exit(0); // 正常終了
}

return 文

return 文は、関数から制御を返すために使用されますが、スレッド関数においても return 文を使用することでスレッドを終了させることができます。

#include <pthread.h>

void *thread_function(void *arg) {
  // スレッド内の処理

  // 終了処理

  return 0; // 正常終了
}

cancel() 関数

cancel() 関数は、別のスレッドから実行中のスレッドを強制終了するために使用されます。

#include <pthread.h>

void *thread_function(void *arg) {
  // スレッド内の処理
}

int main() {
  pthread_t tid;

  pthread_create(&tid, NULL, thread_function, NULL);

  // スレッドを強制終了
  pthread_cancel(tid);

  pthread_join(tid, NULL);

  return 0;
}

シグナル処理を使用してスレッドを終了させることもできます。

#include <pthread.h>
#include <signal.h>

void *thread_function(void *arg) {
  // スレッド内の処理

  // シグナルハンドラを設定
  signal(SIGTERM, signal_handler);

  // シグナルを待つ
  pause();
}

void signal_handler(int sig) {
  // 終了処理

  pthread_exit(0);
}

int main() {
  pthread_t tid;

  pthread_create(&tid, NULL, thread_function, NULL);

  // スレッドに SIGTERM シグナルを送信
  pthread_kill(tid, SIGTERM);

  pthread_join(tid, NULL);

  return 0;
}

上記以外にも、スレッドを終了させる方法はいくつかあります。使用する方法は、状況や環境によって異なります。