web-dev-qa-db-ja.com

C#とブールのスレッドセーフ

私はこの件について非常に混乱しています-bool値の読み取り/切り替えがスレッドセーフかどうか。

    // case one, nothing
    private bool v1;
    public bool V1 { get { return v1; } set { v1 = value; } }

    // case two, with Interlocked on set
    private int v2;
    public int V2 { get { return v2; } set { Interlocked.Exchange(ref v2, value); } }

    // case three, with lock on set
    private object fieldLock = new object();
    private bool v3;
    public bool V3 { get { return v3; } set { lock (fieldLock) v3 = value; } }

それらはすべてスレッドセーフですか?

[〜#〜]編集[〜#〜]

私が読んだもの( クリック )から、ブールの原子性はそれがスレッドセーフであることを保証しません。その後、volatileタイプは役立ちますか?

18

いいえ、すべてがスレッドセーフであるとは限りません。

ケース1は、実際には完全にスレッドセーフではありません。つまり、スレッドセーフではありません。ブール値による操作がアトミックであっても、変数値はキャッシュに格納できるため、マルチコアCPUでは各コアに独自のキャッシュがあるため、値が破損する可能性があります。

さらに進んで、コンパイラーとCPUは、プログラムのロジックに悪影響を与える可能性がある命令の再配列を含む、いくつかの内部最適化を実行できます。

volatileキーワードを追加して、このフィールドがマルチスレッドコンテキストで使用されていることをコンパイラに通知できます。キャッシュと命令の順序変更に関する問題は修正されますが、本当に「スレッドセーフ」なコードは提供されません(書き込み操作はまだ同期されないため)。また、volatileはローカル変数に適用できません。

したがって、マルチスレッドを処理するときは常に、貴重なリソースに対してスレッド同期のいくつかの手法を使用する必要があります。

詳細については this の回答を参照してください。さまざまな手法についての詳細な説明があります。 (例としてintがありますが、実際には問題ではありません。一般的なアプローチについて説明しています。)

25
Yura

少し遅れますが、他の人には役立つはずです。

次の方法で、独自のスレッドセーフなブール値を実装できます。

// default is false, set 1 for true.
private int _threadSafeBoolBackValue = 0;

public bool ThreadSafeBool
{
    get { return (Interlocked.CompareExchange(ref _threadSafeBoolBackValue, 1, 1) == 1); }
    set
    {
        if (value) Interlocked.CompareExchange(ref _threadSafeBoolBackValue, 1, 0);
        else Interlocked.CompareExchange(ref _threadSafeBoolBackValue, 0, 1);
    }
}

プロパティはどこでも使用するようにしてください。int変数に直接アクセスしないでください。

16
lilo0