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


108

私のマルチスレッドasmx Webサービスでは、少数で構成され、としてマークされた独自のタイプ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); 

「揮発性フィールドへの参照は揮発性として扱われません」という警告が表示されますこれについて何を考えればよいですか?

回答:


179

ここには多くの質問があります。それらを1つずつ検討する:

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

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

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

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

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

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

「揮発性フィールドへの参照は揮発性として扱われません」という警告が表示されますこれについて何を考えればよいですか?

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

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

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

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

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

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


1
Interlocked.Exchangeでは、メモリバリアが作成されることも保証されています。たとえば、新しいオブジェクトを作成し、いくつかのプロパティを割り当ててから、Interlocked.Exchangeを使用せずにオブジェクトを別の参照に格納すると、コンパイラーはそれらの操作の順序を混乱させ、2番目の参照にスレッドではなくアクセスする可能性があります。安全。本当にそうですか?Interlocked.Exchangeを使用することには意味がありますか?
Mike

12
@マイク:ローロックマルチスレッドの状況で観察される可能性があることに関しては、私は次の人と同じくらい無知です。答えは、おそらくプロセッサによって異なります。専門家に質問をするか、興味がある場合はその件について読んでください。Joe Duffyの本と彼のブログは、始めるのに良い場所です。私のルール:マルチスレッドを使用しないでください。必要な場合は、不変のデータ構造を使用してください。できない場合は、ロックを使用してください。ロックなしで変更可能なデータが必要な場合にのみ、低ロック手法を検討する必要があります。
Eric Lippert、2010

エリックさん、お返事ありがとうございます。それは確かに私に興味を起こさせます、それが私がマルチスレッディングとロック戦略について本とブログを読んでいて、そしてまた私のコードにそれらを実装しようと試みている理由です。しかし、まだ学ぶべきことがたくさんあります...
Mike

2
@EricLippert「マルチスレッドを使用しない」と「必要な場合は不変のデータ構造を使用する」の間に、「子スレッドに排他的に所有される入力オブジェクトのみを使用させ、親スレッドが結果を消費する」という中間レベルの非常に一般的なレベルを挿入します子供が終わったときだけ」。のようにvar myresult = await Task.Factory.CreateNew(() => MyWork(exclusivelyLocalStuffOrValueTypeOrCopy));
John

1
@ジョン:それはいい考えです。私はスレッドを安価なプロセスのように扱いたいと思っています。それらは、メインプログラムのデータ構造内の制御の2番目のスレッドであるのではなく、ジョブを実行して結果を生成するためにあります。しかし、スレッドが実行している作業の量が多すぎてプロセスのように扱うのが合理的である場合は、それをプロセスにするだけです。
Eric Lippert、2014年

9

同僚が間違っているか、C#言語仕様では理解できないことを彼は知っています。

5.5変数参照の原子性

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

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

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


3
@guffa:はい、私もそれを読みました。これは元の質問「参照の割り当てはアトミックなので、なぜInterlocked.Exchange(ref Object、Object)が必要なのですか?」unaswered
char m

@zebrabox:どういう意味ですか?彼らがいないとき?あなたならどうしますか?
char m

@matti:アトミック操作として値を読み書きする必要がある場合に必要です。
Guffa

.NETでメモリが正しく整列されていないことを心配する必要がある頻度はどれくらいですか。相互運用が多いもの?
Skurmedel 2010

1
@zebrabox:仕様はその警告をリストしていません、それは非常に明確な声明を与えます。参照の読み取りまたは書き込みがアトミックに失敗する、メモリに整列されていない状況の参照がありますか?それは仕様の非常に明確な言語に違反するようです。
TJクラウダー

6

Interlocked.Exchange <T>

指定された型Tの変数を指定された値に設定し、アトミック操作として元の値を返します。

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

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


3

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

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

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

同期オブジェクトを通知する関数

待機機能

連動機能

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

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

  • それを呼び出すスレッド:
    • 命令の並べ替えは行われません(コンパイラ、ランタイム、またはハードウェアによって)。
  • すべてのスレッド:
    • この命令の前に行われるメモリへの読み取りは、この命令が行った変更を認識しません。
    • この命令の後のすべての読み取りには、この命令によって行われた変更が表示されます。
    • この命令の変更後のメモリへのすべての書き込みは、この命令の変更がメインメモリに到達した後に行われます(実行時にこの命令の変更をメインメモリにフラッシュし、ハードウェアにオンタイミングでフラッシュさせないことによって)。
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.