最初の一回だけ初期化したいとき
一日一回スレッド勉強
最初の一回だけ初期化したいとき
以下のように書く
// まったくセンスのない例ですが #include <stdio.h> #include <pthread.h> // この p を static char* p = NULL; static pthread_mutex_t m; void* f(void* _p) { // 最初にここを通ったときだけ初期化したい pthread_mutex_lock(&m); if (p == NULL) { char* tmp = (char*)malloc(10); strcpy(tmp, "hoge"); p = tmp; } pthread_mutex_unlock(&m); *(char**)_p = p; } int main() { pthread_t t0, t1; char* p0; char* p1; pthread_create(&t0, NULL, f, &p0); pthread_create(&t1, NULL, f, &p1); pthread_join(t0, NULL); pthread_join(t1, NULL); printf("%p %p\n", p0, p1); return 0; }
でも
これだと、2回目以降に毎回必要のないロックがかかることになる。
遅い
なので
// ここを volatile にする // (この変数の値はアトミック(つまり、レジスタにだけあってメモリにないということがない変数に)になる) volatile char* p = NULL; pthread_mutex_t m; void* f(void* _p) { // ロックかからない if (p == NULL) { pthread_mutex_lock(&m); // ここからはクリティカルセクション // 一個目の初期化時にここでブロックしたスレッドのために // もう一回 NULL チェック if (p == NULL) { // ここではまだ p に代入しない // 代入したら別スレッドで初期化されていない p が返ってしまう char* tmp = (char*)malloc(10); strcpy(tmp, "hoge"); // クリティカルセクションの最後で代入 p = tmp; } pthread_mutex_unlock(&m); } *(char**)_p = p; }
こういうのを
Double Checked Locking Optimize というらしい
試してみた
#include <stdio.h> #include <memory.h> #include <stdlib.h> #include <pthread.h> #ifdef DCL volatile char* p = NULL; #endif #ifndef DCL static char* p = NULL; #endif pthread_mutex_t m; void* f(void* _p) { #ifdef DCL if (p == NULL) { #endif pthread_mutex_lock(&m); if (p == NULL) { char* tmp = (char*)malloc(10); strcpy(tmp, "hoge"); p = tmp; } pthread_mutex_unlock(&m); #ifdef DCL } #endif *(char**)_p = p; } #define NUM 0x1000 int main() { pthread_t t[NUM]; char* p[NUM]; int i; for (i = 0; i < NUM; i ++) pthread_create(t + i, NULL, f, p + i); for (i = 0; i < NUM; i ++) { pthread_join(t[i], NULL); if (p[0] != p[i]) puts("ERROR!!"); } puts(""); return 0; }
$ gcc -DDCL volatile.c && time ./a.out && gcc volatile.c && time ./a.out volatile.c: In function ‘f’: volatile.c:37: warning: assignment discards qualifiers from pointer target type real 0m0.557s user 0m0.082s sys 0m0.569s real 0m0.504s user 0m0.080s sys 0m0.536s
あれ、全然変わらないなあ。
(追記)このコードはバグッてるみたいです><
基本的にメモリバリアーを理解してないと、DCLパターンは地雷。
http://mkosaki.blog46.fc2.com/blog-entry-552.html
ありがとうございます><
次の勉強はメモリバリアーですね!