Linux スレッド シリーズのパート II (スレッドの作成と識別) では、スレッド ID、2 つのスレッド ID を比較する方法、およびスレッドを作成する方法について説明しました。
この記事では、主にスレッドの終了方法に焦点を当てます。
Linux スレッド シリーズ:パート 1、パート 2、パート 3 (この記事)。
C スレッドのサンプル プログラム
このシリーズのパート II で説明したのと同じ例を挙げると、
#include<stdio.h> #include<string.h> #include<pthread.h> #include<stdlib.h> #include<unistd.h> pthread_t tid[2]; void* doSomeThing(void *arg) { unsigned long i = 0; pthread_t id = pthread_self(); if(pthread_equal(id,tid[0])) { printf("\n First thread processing\n"); } else { printf("\n Second thread processing\n"); } for(i=0; i<(0xFFFFFFFF);i++); return NULL; } int main(void) { int i = 0; int err; while(i < 2) { err = pthread_create(&(tid[i]), NULL, &doSomeThing, NULL); if (err != 0) printf("\ncan't create thread :[%s]", strerror(err)); else printf("\n Thread created successfully\n"); i++; } sleep(5); return 0; }
「sleep()」関数が使用されているのを観察しましたか? sleep() が使用されている理由について質問を受けましたか?もしそうなら、あなたは答えを得るための正しい場所にいます。
上記のコードから sleep() 関数を削除してコンパイルして実行すると、次の出力が表示されます:
$ ./threads Thread created successfully First thread processing Thread created successfully
しかし、sleep() を有効にして実行すると、次のような出力が表示されます:
$ ./threads Thread created successfully First thread processing Thread created successfully Second thread processing
したがって、sleep() 関数を削除すると、ログ「2 番目のスレッド処理」が欠落していることがわかります。
それで、なぜこれが起こるのですか?これは、2 番目のスレッドがスケジュールされる直前に、親スレッド (2 つのスレッドが作成された元) が実行を完了したために発生しました。これは、main() 関数が実行されていたデフォルト スレッドが完了したため、main() が返されたときにプロセスが終了したことを意味します。
スレッド終了
すでに説明したように、各プログラムは、main() 関数が実行されるスレッドである少なくとも 1 つのスレッドで開始されます。したがって、プログラムで実行されるすべてのスレッドの最大寿命は、メイン スレッドの寿命です。そのため、他のすべてのスレッドが終了するまでメイン スレッドを待機させたい場合は、関数 pthread_join() があります。
#include <pthread.h> int pthread_join(pthread_t thread, void **rval_ptr);
上記の関数は、完了するまで親スレッドが終了しないようにします。この関数は親スレッド内から呼び出され、最初の引数は待機するスレッドのスレッド ID で、2 番目の引数は親スレッドが待機するスレッドの戻り値です。戻り値に関心がない場合は、このポインターを NULL に設定できます。
より広いレベルで分類すると、スレッドは次の 3 つの方法で終了できることがわかります。
<オール>ここでの焦点は pthread_exit() にあります。そのプロトタイプは次のとおりです:
#include <pthread.h> void pthread_exit(void *rval_ptr);
したがって、この関数が引数を 1 つだけ受け入れることがわかります。これは、この関数を呼び出すスレッドからの戻り値です。この戻り値は、このスレッドの終了を待っている親スレッドによってアクセスされます。 pthread_exit() 関数によって終了されたスレッドの戻り値は、上で説明した pthread_join の 2 番目の引数でアクセスできます。
C スレッド終了の例
上記の関数を使用する例を見てみましょう:
#include<stdio.h> #include<string.h> #include<pthread.h> #include<stdlib.h> #include<unistd.h> pthread_t tid[2]; int ret1,ret2; void* doSomeThing(void *arg) { unsigned long i = 0; pthread_t id = pthread_self(); for(i=0; i<(0xFFFFFFFF);i++); if(pthread_equal(id,tid[0])) { printf("\n First thread processing done\n"); ret1 = 100; pthread_exit(&ret1); } else { printf("\n Second thread processing done\n"); ret2 = 200; pthread_exit(&ret2); } return NULL; } int main(void) { int i = 0; int err; int *ptr[2]; while(i < 2) { err = pthread_create(&(tid[i]), NULL, &doSomeThing, NULL); if (err != 0) printf("\ncan't create thread :[%s]", strerror(err)); else printf("\n Thread created successfully\n"); i++; } pthread_join(tid[0], (void**)&(ptr[0])); pthread_join(tid[1], (void**)&(ptr[1])); printf("\n return value from first thread is [%d]\n", *ptr[0]); printf("\n return value from second thread is [%d]\n", *ptr[1]); return 0; }
上記のコードでは:
- pthread_create() を使用して 2 つのスレッドを作成しました
- 両方のスレッドの開始関数は同じ、つまり doSomeThing()
- スレッドは、戻り値を持つ pthread_exit() 関数を使用して start 関数から終了します。
- スレッドが作成された後、メイン関数で pthread_join() 関数が呼び出され、2 つのスレッドが完了するのを待ちます。
- 両方のスレッドが完了すると、戻り値は pthread_join() 呼び出しの 2 番目の引数によってアクセスされます。
上記のコードの出力は次のようになります:
$ ./threads Thread created successfully Thread created successfully First thread processing done Second thread processing done return value from first thread is [100] return value from second thread is [200]
したがって、両方のスレッドが完全に実行され、その戻り値がメイン関数でアクセスされることがわかります。