C#のブール読み取り/書き込みアトミックです


84

C#でboolフィールドにアクセスしていますか?特に、ロックをかける必要がありますか?

class Foo
{
   private bool _bar;

   //... in some function on any thread (or many threads)
   _bar = true;

   //... same for a read
   if (_bar) { ... }
}


1
はい、しかし(おそらく)またそうです。はい、boolフィールドへのアクセス/設定はアトミックですが、if操作はアトミックではないため(以下のDror Helperの回答を参照)、ロックも必要になる場合があります。
JPProgrammer 2015

回答:


119

はい。

次のデータ型の読み取りと書き込みはアトミックです:bool、char、byte、sbyte、short、ushort、uint、int、float、および参照型。

見られるようなC#言語仕様

編集:volatileキーワードを理解することもおそらく価値があります。


10
ポインタ自体は、それを再割り当てし、アトミックです(つまり、Foo foo1 = foo2;
user142350 2009

4
@configurator:問題は、正確に何が欲しいかということです。ロックフリープログラムを間違えるのは簡単です。したがって、本当に必要な場合を除いて、より単純なフレームワーク(TPLなど)を使用することをお勧めします。言い換えれば、「揮発性」は間違いではありませんが、トリッキーな(つまり回避されることが望ましい)コードの兆候です。OPは彼が何を望んでいるのかを実際には言っていません、私は揮発性のウィリーニリーを推薦することを躊躇しています。
Eamon Nerbonne 2011

4
ワウ。これは危険な表現です。C++の人々にとって、アトミックとは、読み取りと書き込みも対応するメモリフェンスに囲まれていることを意味します。これは確かにC#には当てはまりません。そうしないと、すべての変数<longに必須であるため、パフォーマンスがひどいものになるためです。ここでのC#用語でのアトミックとは、読み取りまたは書き込みが最終的に発生したときに、それらが壊れた状態になることは決してないということを意味しているようです。しかし、それはいつ「最終的に」であるかについては何も述べていません。
v.oddou 2015年

4
intとlongへの書き込みがアトミックである場合、Interlocked.Add(ref myInt);たとえば?
Mike de Klerk 2015

6
@MikedeKlerk読み取りと書き込みはアトミックですが、別々です。i++はに等しいi=i+1。つまり、アトミック読み取り、加算、アトミック書き込みの順に実行します。別のスレッドはi、読み取り後、書き込み前に変更される可能性があります。たとえばi++、同じiで同時に実行している2つのスレッドは、たまたま同時に読み取り(したがって、同じ値を読み取り)、1つを追加してから、両方が同じ値を書き込み、事実上1回だけ追加する可能性があります。Interlocked.Addはこれを防ぎます。原則として、型がアトミックであるという事実は、書き込みスレッドが1つだけで、読み取りスレッドが多い場合にのみ役立ちます。
Jannis Froese 2016

49

上で述べたように、boolはアトミックですが、それを使用して何をしたいかにも依存することを覚えておく必要があります。

if(b == false)
{
    //do something
}

はアトミック操作ではありません。つまり、現在のスレッドがifステートメントの後にコードを実行する前にb値が変更される可能性があります。


29

ブールアクセスは確かにアトミックですが、それだけではありません。

「不完全に書き込まれた」値の読み取りについて心配する必要はありません-いずれにせよ、それがブール値にとって何を意味するのかは明確ではありません-しかし、少なくとも次の詳細については、プロセッサキャッシュについて心配する必要があります。タイミングが問題です。コアAで実行されているスレッド#1_barにキャッシュがあり、_bar別のコアで実行されているスレッド#2によって更新された場合、ロックを追加するか、_barとして宣言するvolatileか、明示的に呼び出しを挿入しThread.MemoryBarrier()て無効にしない限り、スレッド#1はすぐに変更を確認しません。キャッシュされた値。


1
「どのような場合でも、ブール値にとって何を意味するのかは明確ではありません」バイト全体が同時に書き込まれるため、アトミックでメモリの1バイトにのみ存在するアイテム。複数のバイトに存在するdoubleのようなアイテムとは異なり、1つのバイトが他のバイトの前に書き込まれ、半分書き込まれたメモリ位置を観察できます。
MindStalker 2010

3
MemoryBarrier()は、プロセッサキャッシュを無効にしません。一部のアーキテクチャでは、プロセッサはパフォーマンスのためにメインメモリへの読み取りと書き込みを並べ替えることができます。単一スレッドの観点から、セマンティクスが同じである限り、並べ替えが発生する可能性があります。MemoryBarrier()は、バリアの前に発行されたメモリ操作がバリアの後に終了するような方法で並べ替えられないように、並べ替えを制限するようにプロセッサに要求します。
TiMoch 2013

1
メモリバリアは、ファットオブジェクトを作成し、他のスレッドから読み取られる可能性のあるオブジェクトへの参照を切り替える場合に役立ちます。バリアは、ファットオブジェクトの残りの部分の前に参照がメインメモリで更新されないことを保証します。他のスレッドは、fatオブジェクトが実際にメインメモリで使用可能になる前に、参照の更新を確認しないことが保証されています。var fatObject = new FatObject(); Thread.MemoryBarrier(); _sharedRefToFat = fatObject;
TiMoch 2013

1

私が使用したアプローチは正しいと思いますが、

volatile bool b = false;

.. rarely signal an update with a large state change...

lock b_lock
{
  b = true;
  //other;
}

... another thread ...

if(b)
{
    lock b_lock
    {
       if(b)
       {
           //other stuff
           b = false;
       }
     }
}

目標は基本的に、めったに発生しない大量の状態変更情報を提供するためにオブジェクトをロックする必要があるかどうかを確認するために、反復ごとにオブジェクトを繰り返しロックする必要がないようにすることでした。私が考えるこのアプローチは動作します。そして、絶対的な一貫性が必要な場合は、bboolでvolatileが適切だと思います


4
これは確かに一般的にロックするための正しいアプローチですが、boolがアトミックである場合は、ロックを省略する方が簡単(かつ高速)です。
dbkk 2013年

2
ロックがないと、「大きな状態変化」はアトミックに実行されません。ロック->セット| チェック->ロック->チェックアプローチは、「//その他」コードが「//その他のもの」コードの前に実行されることも保証します。「別のスレッド」セクションには、(それは私の場合である)何回も反復するだけブール値をチェックする必要が、時間のほとんどが、実際に(おそらく競合)ロックを取得されていないと仮定すると、主要なパフォーマンスの勝利だ
stux

1
さて、あなたが持っているならlock()、あなたは必要ありませんvolatile
xmedeko 2018
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.