C++マルチスレッド完全ガイド:効率的な並行プログラミング
マルチスレッドはマルチタスクの一形態であり、マルチタスクとはコンピュータが2つ以上のプログラムを同時に実行できる機能です。一般的にマルチタスクには2種類あります:プロセスベースとスレッドベースです。
プロセスベースのマルチタスクは、プログラムの同時実行を扱います。スレッドベースのマルチタスクは、同一プログラムの異なる部分を同時に実行することを扱います。
マルチスレッドプログラムには、同時に実行できる2つ以上の部分が含まれます。このようなプログラムの各部分をスレッドと呼び、各スレッドは独立した実行パスを定義します。
C++自体にはマルチスレッドアプリケーションの組み込みサポートがありません。代わりに、この機能を提供するためにオペレーティングシステムに完全に依存しています。
このチュートリアルではLinux OSでの作業を前提とし、POSIXを使用してマルチスレッドC++プログラムを作成します。POSIX Threads(Pthreads)は、FreeBSD、NetBSD、GNU/Linux、macOS、Solarisなど多くのUnix系POSIXシステムで利用可能なAPIを提供します。
スレッドの作成
POSIXスレッドを作成するために以下のルーチンを使用します。
#include <pthread.h> pthread_create (thread, attr, start_routine, arg)
pthread_createは新しいスレッドを作成し、実行可能にします。このルーチンはコード内のどこからでも何度でも呼び出せます。パラメータの説明は以下の通りです。
| 番号 | パラメータと説明 |
|---|---|
| 1 | thread サブルーチンから返される新しいスレッドの不透明な一意識別子 |
| 2 | attr スレッド属性を設定するために使用される不透明な属性オブジェクト。スレッド属性オブジェクトを指定するか、デフォルト値の場合はNULL |
| 3 | start_routine スレッドが作成された時点で実行するC++ルーチン |
| 4 | arg start_routineに渡される単一の引数。void型へのポインタキャストとして参照渡しで渡される。引数を渡さない場合はNULL |
プロセスが作成できるスレッドの最大数は実装依存です。一度作成されると、スレッドはピアとなり、他のスレッドを作成できます。スレッド間に暗黙の階層や依存関係はありません。
スレッドの終了
POSIXスレッドを終了するために以下のルーチンを使用します。
#include <pthread.h> pthread_exit (status)
pthread_exitはスレッドを明示的に終了するために使用します。通常、スレッドが作業を完了し、もはや存在する必要がなくなった後に呼び出されます。
main()が作成したスレッドより先に終了し、pthread_exit()で終了した場合、他のスレッドは実行を継続します。そうでない場合、main()が終了すると自動的に終了します。
例:5つのスレッドを作成
#include <iostream>
#include <cstdlib>
#include <pthread.h>
using namespace std;
#define NUM_THREADS 5
void *PrintHello(void *threadid) {
long tid = (long)threadid;
cout << "Hello World! Thread ID, " << tid << endl;
pthread_exit(NULL);
}
int main() {
pthread_t threads[NUM_THREADS];
int rc, i;
for(i = 0; i < NUM_THREADS; i++) {
cout << "main() : creating thread, " << i << endl;
rc = pthread_create(&threads[i], NULL, PrintHello, (void *)i);
if(rc) {
cout << "Error: unable to create thread," << rc << endl;
exit(-1);
}
}
pthread_exit(NULL);
}
-lpthreadライブラリを使ってコンパイルします:
$ gcc test.cpp -lpthread
実行結果:
main() : creating thread, 0 main() : creating thread, 1 main() : creating thread, 2 main() : creating thread, 3 main() : creating thread, 4 Hello World! Thread ID, 0 Hello World! Thread ID, 1 Hello World! Thread ID, 2 Hello World! Thread ID, 3 Hello World! Thread ID, 4
スレッドへの引数渡し
構造体を使って複数の引数を渡す例です。スレッドコールバックはvoidポインタを指すため、あらゆるデータ型を渡せます。
#include <iostream>
#include <cstdlib>
#include <pthread.h>
using namespace std;
#define NUM_THREADS 5
struct thread_data {
int thread_id;
char *message;
};
void *PrintHello(void *threadarg) {
struct thread_data *my_data = (struct thread_data *)threadarg;
cout << "Thread ID : " << my_data->thread_id;
cout << " Message : " << my_data->message << endl;
pthread_exit(NULL);
}
int main() {
pthread_t threads[NUM_THREADS];
struct thread_data td[NUM_THREADS];
int rc, i;
for(i = 0; i < NUM_THREADS; i++) {
cout << "main() : creating thread, " << i << endl;
td[i].thread_id = i;
td[i].message = "This is message";
rc = pthread_create(&threads[i], NULL, PrintHello, (void *)&td[i]);
if(rc) {
cout << "Error: unable to create thread," << rc << endl;
exit(-1);
}
}
pthread_exit(NULL);
}
スレッドの結合と分離
スレッドを結合または分離するための2つのルーチンがあります。
pthread_join (threadid, status) pthread_detach (threadid)
pthread_join()は、指定されたthreadidのスレッドが終了するまで呼び出しスレッドをブロックします。スレッド作成時、その属性の1つで結合可能か分離済みかが決まります。結合可能として作成されたスレッドのみ結合できます。分離済みとして作成されたスレッドは決して結合できません。
例:pthread_joinでスレッド完了を待機
#include <iostream>
#include <cstdlib>
#include <pthread.h>
#include <unistd.h>
using namespace std;
#define NUM_THREADS 5
void *wait(void *t) {
long tid = (long)t;
sleep(1);
cout << "Sleeping in thread " << endl;
cout << "Thread with id : " << tid << " ...exiting " << endl;
pthread_exit(NULL);
}
int main() {
int rc, i;
pthread_t threads[NUM_THREADS];
pthread_attr_t attr;
void *status;
// 初期化とスレッドを結合可能に設定
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
for(i = 0; i < NUM_THREADS; i++) {
cout << "main() : creating thread, " << i << endl;
rc = pthread_create(&threads[i], &attr, wait, (void *)i);
if(rc) {
cout << "Error: unable to create thread," << rc << endl;
exit(-1);
}
}
// 属性を解放し、他のスレッドを待機
pthread_attr_destroy(&attr);
for(i = 0; i < NUM_THREADS; i++) {
rc = pthread_join(threads[i], &status);
if(rc) {
cout << "Error: unable to join," << rc << endl;
exit(-1);
}
cout << "Main: completed thread id :" << i;
cout << " exiting with status :" << status << endl;
}
cout << "Main: program exiting." << endl;
pthread_exit(NULL);
}
C言語
- C++演算子完全ガイド:算術・代入・関係・論理・ビット演算子と実例
- C++コメント完全ガイド:読みやすく保守しやすいコードのためのベストプラクティス
- C++クラステンプレート完全ガイド:実践的なジェネリックプログラミング
- C++プログラミング言語:概要と主要機能
- C++定数とリテラル完全解説:型、構文、ベストプラクティス
- C++演算子完全ガイド:種類、使い方、ベストプラクティス
- C++数値型完全ガイド:実践的な使い方と数学関数
- C++リファレンス完全理解:重要概念とポインタとの違い
- C++テンプレート完全ガイド:ジェネリックプログラミングの基礎
- C++マルチスレッド完全ガイド:効率的な並行プログラミング
- C# マルチスレッド完全マスター:パフォーマンスと効率を向上させる