1. ホーム
  2. c#

[解決済み] 参照代入はアトミックなのに、なぜInterlocked.Exchange(ref Object, Object)が必要なのか?

2022-10-04 23:57:03

質問

マルチスレッドのasmxウェブサービスで、独自のSystemData型のクラスフィールド_allDataを持っていましたが、これはいくつかの要素で構成されています。 List<T>Dictionary<T> とマークされている volatile . システムデータ ( _allData ) は時々リフレッシュされます。 newData という別のオブジェクトを作り、そのデータ構造を新しいデータで埋めることで行います。それが終わると、私はただ

private static volatile SystemData _allData

public static bool LoadAllSystemData()
{
    SystemData newData = new SystemData();
    /* fill newData with up-to-date data*/
     ...
    _allData = newData.
} 

これは、割り当てがアトミックであり、古いデータへの参照を持つスレッドがそれを使い続け、残りは割り当て直後に新しいシステムデータを持つので、うまくいくはずです。しかし、私の同僚は volatile キーワードと単純な代入を使う代わりに、私は InterLocked.Exchange というのは、あるプラットフォームでは参照代入がアトミックであることが保証されないからだそうです。さらに the _allData というフィールドを volatile という

Interlocked.Exchange<SystemData>(ref _allData, newData); 

produces warning "a reference to a volatile field will not be treated as volatile" これについてどう考えればいいでしょうか?

どのように解決すればよいのでしょうか。

ここには数多くの質問があります。一度に1つずつ考えてみましょう。

参照代入はアトミックであるのに、なぜInterlocked.Exchange(ref Object, Object)が必要なのでしょうか?

参照代入はアトミックです。Interlocked.Exchangeは参照渡しを行うだけではありません。変数の現在値を読み込み、古い値を退避させ、新しい値を変数に代入する、という作業をアトミックに行います。

私の同僚は、いくつかのプラットフォームでは参照代入がアトミックであることが保証されないと言いました。私の同僚は正しかったのでしょうか。

いいえ。参照代入は、すべての .NET プラットフォームでアトミックであることが保証されています。

私の同僚は誤った前提から推論しています。それは、彼らの結論が正しくないということですか?

そうとは限りません。あなたの同僚は、悪い理由のために良いアドバイスをしているのかもしれません。もしかしたら、Interlocked.Exchange を使うべき理由が他にあるのかもしれません。ロックフリーのプログラミングは非常に難しく、その分野の専門家が支持する確立されたプラクティスから外れた瞬間に、あなたは雑草の中に入り込み、最悪のレース状態を引き起こすリスクを負っているのです。私はこの分野の専門家でもなければ、あなたのコードの専門家でもないので、どちらか一方に判断することはできません。

produces warning "a reference to a volatile field will not be treated as volatile" これについてどう考えればよいのでしょうか。

一般的になぜこれが問題なのかを理解する必要があります。そうすれば、なぜこの特定のケースで警告が重要でないのかの理解につながります。

コンパイラがこの警告を出す理由は、フィールドをvolatileとマークすることは、"このフィールドは複数のスレッドで更新されることになるからです。このフィールドの値をキャッシュするコードを生成してはいけませんし、このフィールドの読み取りまたは書き込みがプロセッサキャッシュの不整合によって"時間的に前や後ろに移動しないことを確認しなければなりません。

(私は、あなたがすでにこれらすべてを理解していると仮定しています。もし、volatile の意味とそれがプロセッサ キャッシュのセマンティクスにどのように影響するかを詳細に理解していないのであれば、volatile がどのように機能するかを理解しておらず、volatile を使用すべきではないでしょう。ロックフリーのプログラムを正しく作るのは非常に困難です。あなたのプログラムが、偶然に正しいのではなく、それがどのように動作するかを理解しているから正しいのだということを確認してください)。

ここで、volatileフィールドのエイリアスとなる変数を、そのフィールドにrefを渡して作ったとします。呼び出されたメソッドの内部では、コンパイラーは参照が揮発性のセマンティクスを持つ必要があることを知る理由が全くありません! コンパイラは、揮発性フィールドの規則を実装していないメソッドのコードを喜んで生成しますが、変数 は揮発性フィールドです。これはロックフリーのロジックを完全に破壊してしまう可能性があります。 常に は揮発性セマンティクスでアクセスされる。あるときは揮発性で、あるときはそうでないという扱いは意味がありません。 常に そうでなければ、他のアクセスにおける一貫性を保証することはできません。

そのため、これを行うとコンパイラは警告を発します。おそらく、注意深く開発したロックフリーのロジックを完全に台無しにしてしまうからです。

もちろん、Interlocked.Exchange は volatile フィールドを予期して正しいことをするように書かれています。したがって、この警告は誤解を招くものです。 私たちがすべきことは、Interlocked.Exchange のようなメソッドの作者が、メソッドに「ref を取るこのメソッドは変数に volatile セマンティクスを強制するので警告を抑止してください"」という属性を付けることができるような仕組みを実装することでした。 おそらく、コンパイラの将来のバージョンでは、そうすることになるでしょう。