SewiGの日記
2006-06-02 [金] [長年日記]
■ [Programming] pthread
以前、Win32でCreateThreadを使うときは注意しようなんてことを書いたので、今度はUnixのスレッドについて書いてみます。幸い、pthread.hにPOSIXなスレッドが用意されているのでこれを使いましょう。
pthread_create()でスレッドを作成します。このとき引数に作成されたスレッドが処理する関数を渡しておきます。あるスレッドの同期を取りたければ、pthread_join()で処理を待ちます。そしてコンパイルするときは、マルチスレッドなコードであることをコンパイラに教えます。
cc -D_REENTRANT -o thread thread.c -lpthread
これは、マルチスレッドだと動かない処理が含まれると困るからです。再入可能か否か。
例えば以下のような感じでマルチスレッドなコードが書けます。
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <pthread.h> int counter = 0; void *increment() { int i; for (i = 0; i < 10000000; i++) { counter++; } } int main(void) { pthread_t tid1, tid2; pthread_create(&tid1, NULL, increment, NULL); pthread_create(&tid2, NULL, increment, NULL); pthread_join(tid1, NULL); pthread_join(tid2, NULL); printf("%d\n", counter); }
しかし、上記のコードは正常に動作しません。2つのスレッド間で同期が取れていないからです。片方のスレッドで変数を参照して、スレッドが切り替わってもう一方のスレッドが変数を参照してから値をインクリメントして演算結果を変数に書き戻す。そしてスレッドが切り替わって先ほどのスレッドが変数を更新したら、一方の更新が無くなりますよね。lost updateです。
そこでセマフォを利用します。変数をあるスレッドが変数にアクセスしているときはロックしてブロックします。具体的には以下のようにします。
struct { pthread_mutex_t mutex; int value; } counter = {PTHREAD_MUTEX_INITIALIZER, 0}; pthread_key_t private_key; void *increment() { int i; int *privp; pthread_setspecific(private_key, malloc(sizeof(int))); privp = pthread_getspecific(private_key); *privp = 0; for (i = 0; i < 10000000; i++) { pthread_mutex_lock(&counter.mutex); counter.value++; pthread_mutex_unlock(&counter.mutex); privp = pthread_getspecific(private_key); (*privp)++; } printf("%d\n", *privp); } int main(void) { pthread_t tid1, tid2; pthread_key_create(&private_key, NULL); pthread_create(&tid1, NULL, increment, NULL); pthread_create(&tid2, NULL, increment, NULL); pthread_join(tid1, NULL); pthread_join(tid2, NULL); printf("%d\n", counter.value); }
private_keyはセマフォとは直接関係ありませんが一応。