「pthread mutexで排他ロックする方法」の版間の差分
(ページの作成:「マルチスレッドプログラミングでスレッド間で共有データにアクセスするときに、mutex(MUTual EXclusion, ミューテックス)を用いて...」) |
|||
行95: | 行95: | ||
printf("done\n"); | printf("done\n"); | ||
printf("%d\n", counter); | printf("%d\n", counter); | ||
+ | pthread_mutex_destroy(&m); | ||
return 0; | return 0; | ||
} | } |
2014年5月10日 (土) 20:09時点における版
マルチスレッドプログラミングでスレッド間で共有データにアクセスするときに、mutex(MUTual EXclusion, ミューテックス)を用いて、排他ロックを行うことがあります。プログラムに競合状態を引き起こすようなコードがあると、計算の整合性、データの整合性が失われます。競合状態を避ける目的で、クリティカルリージョンをロックで保護します。pthread では、pthread_mutex_tとpthread_mutex_lock, pthread_mutex_unlock を用いて、ロックをコントロールします。
読み方
- mutex
- みゅーてっくす
- 競合状態
- きょうごうじょうたい
- MUTual EXclusion
- みゅーちゃる えくすくるーじょん
- クリティカルセクション
- くりてぃかるせくしょん
- critial section
- くりてぃかるせくしょん
- critial region
- くりてぃかるりーじょん
目次
概要
mutex1.c は、単純に2つのスレッドで1つのカウンタをインクリメントするだけのプログラムです。 mutexのロックがある場合と、ない場合の違いをみてみましょう。
counter = counter + 1
mutex1.c のプログラムのロックとアンロックがない場合、2つのスレッドが同時に counter の値を取り出し、それぞれが数を足して、counter に代入した場合、本来、2つのスレッドがそれぞれ1を足して、2になるところ、結果的に1となりうるプログラムです。
for(i=0; i<loop_max; i++){ // lock なし counter++; }
lockとunlockで囲まれた領域は、ロックによって保護されます。 ロックとアンロックの間にある counter++ は、プロセス内部の処理としてはアトミックに処理されます。
for(i=0; i<loop_max; i++){ if (pthread_mutex_lock(&m) != 0) { err(EXIT_FAILURE, "can not lock"); } counter++; if (pthread_mutex_unlock(&m) != 0) { err(EXIT_FAILURE, "can not unlock"); } }
スレッド1がpthread_mutex_lockでロックしている間は、ほかのスレッド2が同じmutexに対して pthread_mutex_lock をしようとすると、スレッド1がアンロックするまで、スレッド2が待たされることになります。
ここでは、1つのロックしか扱いませんが、複数のmutexを扱う場合には、ロックの順番、アンロックの順番に注意しないと、デッドロック状態に陥ります。
pthread_mutex_lock
pthread_mutex_lock は、以下のように動作します。
- 誰もロックされてない
- ロックして、返る
- 誰かがロックしている
- アンロックされるまで待つ
mutex1.c の例
ソースコード mutex1.c
#include <stdio.h> #include <stdlib.h> #include <err.h> #include <pthread.h> #include <unistd.h> const size_t loop_max = 65535; pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER; int counter = 0; void f1(); void f2(); int main(int argc, char *argv[]) { pthread_t thread1, thread2; int ret1,ret2; ret1 = pthread_create(&thread1,NULL,(void *)f1,NULL); ret2 = pthread_create(&thread2,NULL,(void *)f2,NULL); if (ret1 != 0) { err(EXIT_FAILURE, "can not create thread 1"); } if (ret2 != 0) { err(EXIT_FAILURE, "can not create thread 2"); } printf("execute pthread_join thread1\n"); ret1 = pthread_join(thread1,NULL); if (ret1 != 0) { err(EXIT_FAILURE, "can not join thread 1"); } printf("execute pthread_join thread2\n"); ret2 = pthread_join(thread2,NULL); if (ret2 != 0) { err(EXIT_FAILURE, "can not join thread 2"); } printf("done\n"); printf("%d\n", counter); pthread_mutex_destroy(&m); return 0; } void f1() { size_t i; for(i=0; i<loop_max; i++){ if (pthread_mutex_lock(&m) != 0) { err(EXIT_FAILURE, "can not lock"); } counter++; if (pthread_mutex_unlock(&m) != 0) { err(EXIT_FAILURE, "can not unlock"); } } } void f2() { size_t i; for(i=0; i<loop_max; i++){ if (pthread_mutex_lock(&m) != 0) { err(EXIT_FAILURE, "can not lock"); } counter++; if (pthread_mutex_unlock(&m) != 0) { err(EXIT_FAILURE, "can not unlock"); } } }
コンパイル
cc -lpthread mutex1.c -o mutex1
実行例
% ./mutex1 execute pthread_join thread1 execute pthread_join thread2 done 131070
mutexを使用しないとどうなるのか?
これは、上のコードからmutexのロックとアンロックがないときのプログラムです。 mutex1.c は、単純に2つのスレッドで1つのカウンタをインクリメントするだけのプログラムです。
計算は、65535 * 2 = 131070 となるはずですが、3回実行して、違う結果になっています。
$ /usr/bin/time ./nolock execute pthread_join thread1 execute pthread_join thread2 done 115474 0.00 real 0.00 user 0.00 sys $ /usr/bin/time ./nolock execute pthread_join thread1 execute pthread_join thread2 done 111517 0.00 real 0.00 user 0.00 sys $ /usr/bin/time ./nolock execute pthread_join thread1 execute pthread_join thread2 done 122026 0.00 real 0.00 user 0.00 sys
mutexを使用した場合、どうなるのか?
mutex でカウンタの処理をアトミックにした場合、3回とも正しい結果になっています。 プログラムが単純過ぎて、比較の意味はほとんどありませんが、ロックのある場合とない場合で、ほんの少しだけ、処理時間がロックありの場合、多くなっていることがわかります。
$ /usr/bin/time ./lock execute pthread_join thread1 execute pthread_join thread2 done 131070 0.01 real 0.00 user 0.00 sys $ /usr/bin/time ./lock execute pthread_join thread1 execute pthread_join thread2 done 131070 0.01 real 0.00 user 0.00 sys $ /usr/bin/time ./lock execute pthread_join thread1 execute pthread_join thread2 done 131070 0.01 real 0.01 user 0.00 sys
まとめ
- クリティカルリージョンは、mutexで保護しましょう。
- mutex で保護する領域は、最小限にしましょう。
- 複数の mutex を使用する場合には、ロックとアンロックの順番に気をつけましょう。