

C言語で、2つのスレッドを同期するに、下記のようなプログラムを書きました。

[code]

    volatile int hoge = 0;
    //...
    void fuga1() // スレッド1で動いている
    {
        while (1)
        {
            while (hoge == 0) // 値が変わるまで待つ
                sleep(1);
            puts("fuga1!");
            hoge = 0;
        }
    }
    
    void fuga2() // スレッド2で動いている
    {
        while (1)
        {
            sleep(1000); // 1秒待つ
            hoge = 1;
        }
    }
    
[/code]

このプログラムは特に問題なく動いていて、1秒毎に「fuga1」が出力されています。

しかし、「これは`volatile`の正しい使い方ではない」、「スレッド間の同期に`volatile`を使うな」とは聞いたことあります。具体的になぜなのか、誰か教えていただけますでしょうか。


CC BY-SA 3.0
2014-12-12T08:52:37.390
1538
2021-09-03T23:57:57.553
1077
question
c
なぜスレッド間の同期にvolatileを使ってはいけない？


`volatile` はあくまでコンパイラの最適化によって変数が消されてしまうのを防ぐもので、スレッドセーフにさせるための機能ではないからだと思います。
つまり、`volatile` でスレッドセーフだと「間違った」解釈をしてはいけない、という理由ではないかと。


CC BY-SA 3.0
2014-12-12T09:28:37.753
1545
2014-12-12T09:28:37.753
41
1538
answer


他の回答でも述べられていますが、`volatile`
はコンパイラの最適化の影響避けるためのもので、それ以外の何者でもなく、スレッドの同期をとるために用いるのは適切ではありません。

以下のブログ記事を参照すると、なぜ適切でないのかが理解できると思います。

<http://i-saint.hatenablog.com/entry/20101005/1286208402>

なお、スレッド間で同期をとりたい場合には、`条件変数（pthread_cond）`を用いるのが適切だと思います。 以下は `pthread_cond`
を用いた場合のコードです。

[code]

    int hoge = 0;
    pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
    pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
    //...
    void fuga1() // スレッド1で動いている
    {
        while (1)
        {
            pthread_mutex_lock(&mutex);
            while (hoge == 0) // 値が変わるまで待つ
                pthread_cond_wait(&cond, &mutex);
            hoge = 0;
            pthread_mutex_unlock(&mutex);
            puts("fuga1!");
        }
    }
    
    void fuga2() // スレッド2で動いている
    {
        while (1)
        {
            sleep(1000); // 1秒待つ
            pthread_mutex_lock(&mutex);
            hoge = 1;
            pthread_cond_signal(&cond);
            pthread_mutex_unlock(&mutex);
        }
    }
    
[/code]


CC BY-SA 3.0
2014-12-12T09:43:33.123
1546
2014-12-17T13:20:48.080
2014-12-17T13:20:48.080
1024
1024
1538
answer


volatile変数とマルチスレッドとの関係についての押し問答（前編） <http://d.hatena.ne.jp/yohhoy/20121016/p1>

こちらのブログも参考になると思いますがvolatileが環境依存で動かなくなると言う問題がある事と最近の環境だと未定義動作として定義されている事が使うなの理由の様です。


CC BY-SA 3.0
2014-12-12T11:57:06.850
1551
2014-12-12T11:57:06.850
2129
1538
answer


`volatile`変数によるスレッド間同期は、あらゆる環境（コンパイラ、OS）上で期待通り動作する **保証がなされない** ためです。

質問にあるとおり、特定OS上の特定コンパイラでは期待通り動くこともありますが、`volatile`変数＝スレッド間同期として機能することを保証してくれる標準規格（仕様）は存在しません。例えばC言語の言語仕様でもそのような定義は存在しませんし、C言語のうえにくるPOSIX仕様やOpenMP仕様などでもそういった定義は存在しません。

`volatile`変数の振る舞いについてとりわけ誤解を招いているのが、仕様上の保証が無いにも関わらず、多くの環境では`volatile`変数がスレッド間同期として
**機能しているように見える**
ことです。しかし、あくまで偶然に期待通り動作したというだけですから、「これはvolatileの正しい使い方ではない」、「スレッド間の同期にvolatileを使うな」に繋がります。

JPCERTの [POS03-C. volatile を同期用プリミティブとして使用しない](https://www.jpcert.or.jp/sc-
rules/c-pos03-c.html) も参照ください。また @kazuto
さんが参照された記事はC++言語についての言及でしたが、C言語についてもそのまま適用できますので参考にください。

補足：もしx86アーキテクチャ（x86-64含む）上でMicrosoft Visual C++
2005以降を使われているならば、[VC++の独自仕様拡張](http://msdn.microsoft.com/ja-
jp/library/12a04hfd%28v=vs.80%29.aspx)として`volatile`変数＝スレッド間同期として機能します。ただし、新しいC++11/C11言語仕様の策定にともない、アトミック変数が標準定義されましたので、[この独自拡張を積極的に使用すべきではありません](http://msdn.microsoft.com/ja-
jp/library/jj204392.aspx)。


CC BY-SA 3.0
2014-12-12T13:55:30.223
1554
2014-12-12T14:01:05.767
2014-12-12T14:01:05.767
49
49
1538
answer