C 言語でミューテックスロックを使用する
この記事では、C 言語でミューテックスロックを利用する方法をいくつか説明します。
pthread_mutex_t
と pthread_mutex_lock
関数を使ってコードの重要な部分をガードする
スレッドはアドレス空間を共有しているため、グローバル変数のような共有データの変更は同期化されていなければなりません。以下のコードでは、pthread_create
呼び出しで 4つの追加スレッドを作成し、func3
を実行の開始点として渡していることに注意してください。func3
はグローバル変数 shared
を 1つずつ変更し、10000 回の for
ループを繰り返します。したがって、4つのスレッドが shared
の値を 10000 ずつインクリメントすると、プログラムは 40000 を出力するはずです。
以下のコードを実行すると、結果は何らかの乱数の整数になりますが、40000 は出力されません。この挙動は一般的に競合状態に分類され、与えられたスレッドが互いに相談せずに共有変数にアクセスしていることを暗示しています。す。つまり、ループ
の実行がインターリーブすると、共有変数へのアクセスとストアが不整合になり、最終的に不正確な合計が生成されます。
複数のスレッドがメモリ上の同じオブジェクトを変更するコード部分はクリティカルセクションと呼ばれます。一般的に、クリティカルセクションは、現在のスレッドが実行を終了するまで他のスレッドを強制的に待たせ、すべてのスレッドが正しいインクリメント値を取得することを保証する何らかのタイプのロックで保護されている必要があります。Mutex は、func3
の for
ループのようにクリティカルセクションを保護するために利用できるロックタイプの一つです。
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#ifndef NUM_THREADS
#define NUM_THREADS 4
#endif
int shared = 0;
void* func3(void* param) {
printf("Incrementing the shared variable...\n");
for (int i = 0; i < 10000; ++i) {
shared += 1;
}
return 0;
}
int main() {
pthread_t threads[NUM_THREADS];
for (int i = 0; i < NUM_THREADS; ++i) {
pthread_create(&threads[i], NULL, func3, NULL);
}
for (int i = 0; i < NUM_THREADS; ++i) {
pthread_join(threads[i], NULL);
}
printf("%d\n", shared);
exit(EXIT_SUCCESS);
}
出力:
Incrementing the shared variable...
Incrementing the shared variable...
Incrementing the shared variable...
Incrementing the shared variable...
30384
今回は POSIX スレッドライブラリとその組み込みの pthread_mutex_t
型を利用します。pthread_mutex_t
型変数は通常、static
格納期間として宣言されています。ミューテックスは使用する前に一度だけ初期化する必要がある。mutex が static
と宣言されている場合、PTHREAD_MUTEX_INITIALIZER
マクロを用いて初期化する必要があります。mutex が初期化されると、スレッドは pthread_mutex_lock
と pthread_mutex_unlock
関数を利用することができます。pthread_mutex_lock
は引数として渡された mutex オブジェクトをロックします。既にミューテックスがロックされていた場合は、ミューテックスが利用可能になるまで呼び出したスレッドはブロックされます。mutex のロックを解除するには pthread_mutex_unlock
を呼び出す必要があります。同じミューテックスを待っているスレッドがある場合、スケジューリングポリシーによってどちらのスレッドがオブジェクトロックを受けるかが決まります。最後に、4つのスレッドのそれぞれに対して pthread_join
を呼び出して shared
という整数を出力します。
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#ifndef NUM_THREADS
#define NUM_THREADS 4
#endif
int shared = 0;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
void* func3(void* param) {
pthread_mutex_lock(&mutex);
printf("Incrementing the shared variable...\n");
for (int i = 0; i < 10000; ++i) {
shared += 1;
}
pthread_mutex_unlock(&mutex);
return 0;
}
int main() {
pthread_t threads[NUM_THREADS];
for (int i = 0; i < NUM_THREADS; ++i) {
pthread_create(&threads[i], NULL, func3, NULL);
}
for (int i = 0; i < NUM_THREADS; ++i) {
pthread_join(threads[i], NULL);
}
printf("%d\n", shared);
exit(EXIT_SUCCESS);
}
出力:
Incrementing the shared variable...
Incrementing the shared variable...
Incrementing the shared variable...
Incrementing the shared variable...
40000