C# の long は読み書き Atomic じゃないから Interlocked 使おう

複数のスレッドから触られる long 変数(64bit変数)の取り扱い。 Java メインの人は引っかかりそうなのでメモ。

Java ではできる「volatile long」が C# では未サポート(コンパイルエラーになる)なので、 long 変数への Atomic な読み書きをするには読み書き部を lock するか、もしくは Interlocked を使って次の通りにする。

// hoge : マルチスレッドから触りたい変数, local_hoge : スレッドローカルな変数
long local_hoge = Interlocked.Read(ref hoge);   // long の読み込み hoge → local_hoge
Interlocked.Exchange(ref hoge, local_hoge);     // long の書き込み hoge ← local_hoge

Java では volatile long としておけば、読み書きに対する Atomic 性は保証されているので、 JavaC# をうろうろしている人にはちょっとした罠。
C# では 32bit 単位のメモリアクセスを想定しているのか、それを越えるサイズの long や double の Atomic なアクセスは言語機能としてはカバーしない。ライブラリを使えというスタンスな模様。
Java よりハードウェアに近いそういうトレードオフなんだろう。

lock が速いか Interlocked が速いかは評価していないけれど、たぶん後者が速いんだろう。

JavaのAtomicXXXX相当のクラスが欲しいなぁ)

マルチスレッド環境でlongを共有するときの注意 (C#編) - No hack, no life.
volatile (C# リファレンス)
Interlocked クラス (System.Threading)

2016/7/14 追記
Interlocked.Read メソッド (Int64) (System.Threading) の解説によると

解説
The Read method is unnecessary on 64-bit systems, because 64-bit read operations are already atomic.On 32-bit systems, 64-bit read operations are not atomic unless performed using Read.

The Read method and the 64-bit overloads of the Increment, Decrement, and Add methods are truly atomic only on systems where a System.IntPtr is 64 bits long.On other systems, these methods are atomic with respect to each other, but not with respect to other means of accessing the data.Thus, to be thread safe on 32-bit systems, any access to a 64-bit value must be made through the members of the Interlocked class.

『32bit 環境だと 64bit 変数は atomic にアクセス出来ないから Interlocked 使え』とのこと。
64bit 環境だと最適化されて Interlocked.Read とかは空メソッド相当になるんだろうから、 volatile long とすればコンパイラが頑張って Interlocked 経由の操作に置き換えてくれたらいいのに。