lockにおける排他処理をしなかった場合の挙動
id:kosakとともに、実験を行った結果を。
例えば、マルチスレッドにおいて
volatile int num = 0; func(){ for(i = 0; i < 10000; i++) num++; } int main(){ pthread_t t[THREAD]; for(int i = 0; i < THREAD; i++) pthread_create(&t[i], NULL, func, NULL); for(int i = 0; i < THREAD; i++) pthread_join(&t[i], NULL); printf("%d\n", num); }
とやると、numの値がTHREAD * 10000よりも小さくなることがあるのは常識かもしれない。numのメモリアクセスをlock等で排他処理してやらないと、読みだしと書き込みの間で同期バグが起きる可能性があるからだ。
例えばこれはどうだろうか。
volatile int num = 0; int array[THREAD*10000]; func(){ for(i = 0; i < 10000; i++){ int tmp = num++; array[tmp]++; } }
この場合も同様に、もちろんながらnumの値がTHREAD*10000よりも小さくなるのは事実だが、では0から順にarrayの値は歯抜けにならずに埋まるだろうか。
これが埋まらない可能性があるというのが今日の実験結果。なぜなら、tmp = num++のところで、tmpに代入を行うnumとincrementするnumを別々にloadする可能性があるからだ。これは少なくともVisual Studio 2008の吐いたアセンブリにおいて確認した。さらに問題は、numのvolatileがついている場合はこの部分の最適化が抑制されてしまい、最適化オプションO2においても歯抜けが発生する。ただし、volatileを取ってO2を付けると歯抜けにはならない。
これともう一つのバグ(プロトタイプ宣言をしなかったことによる自動型変換によるバグ)で、今日の一日の大半がつぶれた・・・・