スレッド同期を使って複数スレッドで番号を順番に出力する方法(C言語・pthread実装例)
本記事では、複数のスレッドを使って数字を正しい順序で出力する方法を解説します。まず n 個のスレッドを作成し、それらを同期させます。基本的な考え方は、「1番目のスレッドが 1 を出力し、次に2番目のスレッドが 2 を出力する」というように、各スレッドが順番に担当する番号を出力していくというものです。あるスレッドが出力処理を行っている間は共有リソースがロックされるため、他のスレッドがその領域にアクセスすることはできません。
同期の仕組み:ミューテックスと条件変数
このプログラムでは、POSIX スレッド(pthread)が提供する次の2つの同期機構を利用しています。
- ミューテックス(pthread_mutex_t):クリティカルセクション(同時に1つのスレッドしか入れない領域)へのアクセスを排他制御し、競合状態を防ぎます。
- 条件変数(pthread_cond_t):自分の順番ではないスレッドを待機状態にし、順番が回ってきたタイミングで再開させます。
処理の流れ
- 各スレッドは起動時に自分のスレッド番号を受け取ります。
- ミューテックスでロックを取得した後、自分の番号と共有カウンタ
countが一致しない場合はpthread_cond_wait()で待機します。 - 自分の番になれば
printf()で番号を出力し、カウンタを更新します。 pthread_cond_signal()で次のスレッドへ通知し、ロックを解放して次のループへ進みます。
サンプルコード
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t* cond = NULL;
int threads;
volatile int count = 0;
void* sync_thread(void* num) { //スレッドを同期するための関数
int thread_number = *(int*)num;
while (1) {
pthread_mutex_lock(&mutex); //セクションをロック
if (thread_number != count) { //スレッド番号がcountと一致しなければ、
//1つを除く全スレッドを待機状態にする
pthread_cond_wait(&cond[thread_number], &mutex);
}
printf("%d ", thread_number + 1); //スレッド番号を出力
count = (count+1)%(threads);
// 次のスレッドへ通知
pthread_cond_signal(&cond[count]);
pthread_mutex_unlock(&mutex);
}
return NULL;
}
int main() {
pthread_t* thread_id;
volatile int i;
int* thread_arr;
printf("\nEnter number of threads: ");
scanf("%d", &threads);
// 条件変数・スレッドID・サイズthreadsの配列にメモリを確保
cond = (pthread_cond_t*)malloc(sizeof(pthread_cond_t) * threads);
thread_id = (pthread_t*)malloc(sizeof(pthread_t) * threads);
thread_arr = (int*)malloc(sizeof(int) * threads);
for (i = 0; i < threads; i++) { //スレッドを生成
thread_arr[i] = i;
pthread_create(&thread_id[i], NULL, sync_thread, (void*)&thread_arr[i]);
}
// スレッドの終了を待機
for (i = 0; i < threads; i++) {
pthread_join(thread_id[i], NULL);
}
return 0;
}
実行結果
コンパイル時には pthread ライブラリをリンクするため -lpthread オプションが必要です。また、このプログラムは無限ループで動作し続けるため、停止するには Ctrl+C などで中断してください。
$ g++ test.cpp -lpthread $ ./a.out Enter number of threads: 5 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5 ... ... ...
まとめ
このように、ミューテックスによる排他制御と条件変数による待機・通知を組み合わせることで、複数スレッドの実行順序を厳密に制御できます。共有カウンタ count を基準に「今は誰の番か」を判定し、該当スレッドだけが出力を行う設計がポイントです。この手法は、生産者・消費者問題や順序付きタスク実行など、さまざまなマルチスレッドプログラミングの場面で応用できます。
-
C言語で自然数を列方向に出力するプログラムの作り方
プログラムの概要 本記事では、C言語を使って自然数を列方向(縦方向)に並べたパターンを出力する方法を解説します。完成イメージは以下のとおりです。 1 2 6 3 7 10 4 8 11 13 5 9 12 14 15 一見複雑に見えるこの数列も、行と列の関係さえ理解できれば、シンプルなループ処理だけで実現できます。 アルゴリズムの考え方 変数 i は行番号、j は列番号を表し、ここでは5行分のパターンを作成します。 外側のループで各行(i = 1〜5)を順に処理する 内側のループに入る前に、k を i の値で初期化する 内側のループで各列(j = 1〜i)を処理する 現在の k の値を表示
-
C言語で非平方数(完全平方数以外の数)を出力する方法
プログラムの概要ある数の「平方」とは、その数にその数自身を掛け合わせた値のことです。「平方数」または「完全平方数」とは、ある整数を二乗することで得られる整数を指します。完全平方数は自然数を二乗した数であり、具体的には以下のような数になります。1, 4, 9, 16, 25, 36, 49, 64, 81, 100以下は、1から100までの各完全平方数とその平方根の一覧です。√1 = 1 (12 = 1 であるため) √4 = 2 (22 = 4 であるため) √9 = 3 (32 = 9 であるため) √16 = 4 (42 = 16 であるため) √25 = 5 (52 = 25 であるため