マルチスレッドでロックをかけずに読み書きするとどうなんのか

久しぶりの日記だがメモ代わりに書いとこうと思ったので書く。


マルチスレッドで同じ変数の読み書きするときはロックしましょうねーって教わったのは確かなんだが、なんで読むときもロックかけんのー?って思ったので動作確認してみた。
プログラムはこんな感じ

#include <iostream>
#include <pthread.h>
using namespace std;

typedef long long VAL;
volatile VAL g_val = 0;

void *threadFuncWrite(void *args)
{
  for(int i=0; i < 1000000; ++i){
    g_val = (i&1)? 0 : 0x1234abcd12345678LL;
  }
  return NULL;
}

void *threadFuncRead(void *args)
{
  for(int i=0; i < 1000000; ++i){
    VAL g = g_val;
    if(g!=0 && g!=0x1234abcd12345678LL){
      cout << hex << g << endl;
    }
  }
  return NULL;
}


int main(int argc, char **argv)
{
  pthread_t thw, thr;

  pthread_create(&thw, NULL, threadFuncWrite, NULL);
  pthread_create(&thr, NULL, threadFuncRead, NULL);
  
  pthread_join(thw, NULL);
  pthread_join(thr, NULL);

  return 0;
}

g_valに0か0x1234abcd12345678を書き込むスレッドと、g_valの値を読んで0か0x1234abcd12345678以外の値になってたらその値を出力するスレッド、の2つを作って走らせてるだけ。
これを何回か実行してみると、

$ ./a.exe
12345678
1234abcd00000000
12345678
12345678
12345678
1234abcd00000000
12345678
12345678
12345678
12345678
12345678
12345678
1234abcd00000000
1234abcd00000000
12345678

おぉー値が狂っておる!
再現しないときはループさせる数増やせばすぐに分かると思う。
ちなみにintとかで試したときは起こらなかったんで、1命令で読み込めるビット数とかその辺りが関係するんだろーなーとか思いつつもそこまで詳しい話は分からんかった。


とりあえず読み込みのときもロックしなくちゃいけないんだなーって思いましたまる。