118

私のマルチスレッドasmxWebサービスには、自分のタイプ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); 

「揮発性フィールドへの参照は揮発性として扱われません」という警告が表示されます。これについてどう思いますか?

4

4 に答える 4

197

ここにはたくさんの質問があります。それらを一度に1つずつ検討します。

参照の割り当てはアトミックですが、なぜInterlocked.Exchange(ref Object、Object)が必要なのですか?

参照の割り当てはアトミックです。Interlocked.Exchangeは、参照の割り当てだけを行いません。変数の現在の値を読み取り、古い値を隠し、新しい値を変数に割り当てます。これらはすべて不可分操作です。

私の同僚は、一部のプラットフォームでは、参照の割り当てがアトミックであることが保証されていないと述べました。私の同僚は正しかったですか?

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

私の同僚は誤った前提から推論しています。それは彼らの結論が間違っていることを意味しますか?

必ずしも。あなたの同僚は悪い理由であなたに良いアドバイスをしているかもしれません。おそらく、Interlocked.Exchangeを使用する必要がある他の理由があります。ロックフリープログラミングはめちゃくちゃ難しく、この分野の専門家によって支持されている確立された慣行から離れた瞬間、あなたは雑草の中にいて、最悪の種類の競合状態の危険を冒しています。私はこの分野の専門家でもあなたのコードの専門家でもないので、どちらの方法でも判断することはできません。

「揮発性フィールドへの参照は揮発性として扱われません」という警告が表示されます。これについてどう思いますか?

これが一般的に問題である理由を理解する必要があります。これにより、この特定のケースで警告が重要でない理由を理解できます。

コンパイラがこの警告を出す理由は、フィールドを揮発性としてマークすることは、「このフィールドは複数のスレッドで更新される予定です。このフィールドの値をキャッシュするコードを生成しないでください。また、このフィールドは、プロセッサキャッシュの不整合によって「時間的に前後に移動」することはありません。

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

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

したがって、これを行うと、コンパイラは警告を発します。これは、慎重に開発されたロックフリーロジックを完全に台無しにする可能性があるためです。

もちろん、Interlocked.Exchange、揮発性フィールドを予期して正しいことを行うように作成されています。したがって、警告は誤解を招く可能性があります。私はこれを非常に後悔しています。Interlocked.Exchangeのようなメソッドの作成者が、「refを受け取るこのメソッドは変数に揮発性セマンティクスを適用するため、警告を抑制します」という属性をメソッドに設定できるメカニズムを実装する必要があります。おそらく、コンパイラの将来のバージョンでは、そうする予定です。

于 2010-02-03T16:20:07.780 に答える
9

あなたの同僚が間違っているか、C#言語仕様が知らないことを彼は知っています。

変数参照のアトミシティ

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

したがって、破損した値を取得するリスクなしに、揮発性参照に書き込むことができます。

もちろん、一度に複数のスレッドがそれを行うリスクを最小限に抑えるために、どのスレッドが新しいデータをフェッチするかを決定する方法に注意する必要があります。

于 2010-02-03T13:32:42.857 に答える
7

Interlocked.Exchange <T>

指定されたタイプTの変数を指定された値に設定し、元の値を不可分操作として返します。

変更して元の値を返します。変更したいだけなので役に立たず、Guffaが言ったように、すでにアトミックです。

プロファイラーがアプリケーションのボトルネックであることが証明されていない限り、ロックを解除することを検討する必要があります。コードが正しいことを理解して証明する方が簡単です。

于 2010-02-03T13:40:48.067 に答える
4

Iterlocked.Exchange()アトミックであるだけでなく、メモリの可視性も処理します。

次の同期関数は、適切なバリアを使用してメモリの順序を確認します。

クリティカルセクションに出入りする関数

同期オブジェクトに信号を送る関数

待機機能

連動機能

同期とマルチプロセッサの問題

これは、原子性に加えて、次のことを保証することを意味します。

  • それを呼び出すスレッドの場合:
    • 命令の並べ替えは行われません(コンパイラー、ランタイム、またはハードウェアによって)。
  • すべてのスレッドの場合:
    • この命令の前にメモリからの読み取りは行われず、この命令の後に発生した(この命令を呼び出したスレッドによる)メモリへの変更が表示されます。これは明白に聞こえるかもしれませんが、キャッシュラインは書き込まれた順序ではなくメインメモリにフラッシュされる可能性があります。
    • この命令の後のすべての読み取りには、この命令によって行われた変更と、この命令の前に(この命令を呼び出したスレッドによって)行われたすべての変更が表示されます。
    • この命令後のすべてのメモリへの書き込みは、この命令の変更がメインメモリに到達した後に行われます(この命令の変更が完了したらメインメモリにフラッシュし、ハードウェアにオンタイミングでフラッシュさせないようにします)。
于 2016-12-29T15:12:27.747 に答える