2016-06-01 8 views
11

another questionの回答を書くと、興味深いことがいくつか出てきましたが、今は理解できません。Interlocked.Increment(ref long value)は32ビットシステムで動作します。私に説明させてください。 32ビット環境用にコンパイルするとき32ビット環境で64ビット変数のアトミックインクリメント

ネイティブInterlockedIncrement64が現在利用可能ではない、OK、必要に応じて.NETにあなたがメモリを揃えることができないので、それが理にかなって、それがから呼び出すことが、彼らはそれを落とした後、を管理していました。

.NETで

我々は64ビットの変数を参照してInterlocked.Increment()を呼び出すことができ、(我々はFieldOffsetStructLayoutを使用することができる場合も、構造中に例えば)我々はまだ、そのアライメントに関する制約を持たないが、ドキュメントはdoesnのどのような制限(AFAIK)も言及していない。それは魔法です、それは動作します!

ハンスアンパッサンInterlocked.Increment()は、JITコンパイラによって認識特別方法であり、それは、同じ制限をInterlockedIncrement64を共有InterlockedExchangeAdd64マクロあるFastInterlockExchangeAddLongを呼び出すれるCOMInterlocked::ExchangeAdd64()にコールを発することに留意。

今、私は困惑しています。

1秒間の管理環境を忘れてネイティブに戻ってください。なぜInterlockedIncrement64は機能しませんが、InterlockedExchangeAdd64は機能しますか?組み込み関数が利用できないとInterlockedExchangeAdd64作品は、それはInterlockedExchangeAdd64への呼び出しとして実施することができる場合InterlockedIncrement64は...、マクロです

さんが戻っ管理へ行こう:原子64ビットの増分が32ビットシステム上で実装されていますか?私は文章があると思います"この関数は他のインターロック関数への呼び出しに関してはアトミックです"が重要ですが、それでもコードはありませんでした。組み込み関数が利用できないときのがWinBase.hからInterlockedExchangedAdd64実装を選択してみましょう:

FORCEINLINE 
LONGLONG 
InterlockedExchangeAdd64(
    _Inout_ LONGLONG volatile *Addend, 
    _In_ LONGLONG Value 
    ) 
{ 
    LONGLONG Old; 

    do { 
     Old = *Addend; 
    } while (InterlockedCompareExchange64(Addend, 
              Old + Value, 
              Old) != Old); 

    return Old; 
} 

それはどのように読み取り/書き込みのため、原子することができますか?

+0

「InterlockedIncrement64」は動作しませんが、「InterlockedExchangeAdd64」は「?独自の答えは、マネージコードがネイティブのWin32 APIを直接呼び出すことはできず、すべてが動作することを期待しているという点では正しいです。どちらもうまくいきません。管理ヘルパーを使用する必要があります。現在、マネージヘルパーの実装はネイティブコードなので、ネイティブ関数を呼び出します。コンパイル時にマクロと組み込み関数が解決されるため、CLRのビット数が重要です。 –

+0

はい、ただし32ビットJITは、InterlockedIncrement64と同じ制限(ネイティブ)を持つInterlockedExchangeAdd64を呼び出します。私が理解できなかったことは、(マネージコードのために呼び出されたときのメモリの整列のために)それがどのように行われるかです。 32ビットの実装ではInterlockedCompareExchange64を使用しています... ... hmmm ....アトミックではないかもしれません(結果を返すために...) –

+1

* "読み書きのためにはどうしたらアトミックになりますか?" * InterlockedExchangeAdd64ヒントのドキュメント"この関数は、メモリ操作が順番に完了するようにするために、完全なメモリバリア(またはフェンス)を生成します。" *上記の実装は、 'InterlockedCompareExchange64'を呼び出していることに注意してください。 32ビットのビルドでは、これは 'LOCK'プレフィックスを持つ' CMPXCHG8B'命令を発行します。これにより、命令がアトミックに実行されます。ロックされた書き込みがない状態でロックされた読み込みを得ることはないので、書き込み先はアトミックです。 –

答えて

9

InterlockedExchangeAdd64()は、Trailに続いてWinNt.h SDKヘッダーファイルに移動します。ターゲットアーキテクチャに応じて、さまざまなバージョンが表示されます。

これは、一般的に崩壊:VC /で宣言された固有のコンパイラにバックを渡し

#define InterlockedExchangeAdd64 _InterlockedExchangeAdd64 

は/ intrin.hとコンパイラのバックエンドによって実装含まれています。

つまり、CLRの異なるビルドには異なる実装があります。 x86、x64、Itanium、ARM、ARM8、PowerPCなど、私の頭の上には何年も前から多くのことがありましたが、Appleがそれを無関係にする前にWindowsCEの起動に使用されていたものがあります。 x86の場合、これは最終的に、ミスアライメントされた64ビット変数を処理できる専用のプロセッサ命令であるLOCK CMPXCHNG8Bによって処理されます。私は、他の32ビットプロセッサ上でどのように見えるかを見るためのハードウェアを持っていません。

管理されたコードのターゲットアーキテクチャはコンパイル時に釘付けにされないことに注意してください。 MSILを実行時にターゲットに適応させるのはジッタです。/ clr:pureの代わりに/ clrを使ってコンパイルし、x86とx64だけが動くことができるのであれば、ターゲットを選ぶ必要があるので、C++/CLIプロジェクトにはあまり関係しません。しかし、配管工事はどこにでもあるので、マクロはそれほど有用ではありません。

+0

申し訳ありませんが、私はそれを取得しません。 InterlockedExchangeAdd64の実装が32ビット(イントリンシックなし!)でうまく動作すると仮定すると、InterlockedIncrement64マクロも同じ方法(32ビット管理環境)で実装できます。これを拡張すると、64ビットのネイティブ関数も32ビットで確実に実装できます。どうやって?!まあ、私は言いました_ "この関数は他のインターロックされた関数への呼び出しに関して原子**です。" _はキーですが、パスの後には私はそれを行うための_special_コードを見ることができません –

+0

ハングアップがあり、マクロはコンパイル時にのみ動作します。コードがCLRの内部にある場合はうまくいきますが、実行時にターゲットアーキテクチャと一致する適切なバージョンが用意されています。したがって、ヘルパー関数を呼び出すと、常にそのジョブが完了します。異なるプラットフォーム上で実行できるので、自分の.NETアセンブリではコンパイル時にはうまくいきません。実行時にインラインアセンブリコード(LOCK XADD)に変換されても、ターゲットプラットフォームで許可されても、スイートスポットになります。どちらの場合でも保証を提供するプロセッサーです。 –

+0

もちろん、私はコンパイル時にマクロを解決しなければならないことを理解しています。 Win32を対象としたC++/CLIの混在プロジェクトがあるとします。 Interlocked :: Increment(long long)を呼び出すことができます。実行時にJITはその呼び出しを独自のヘルパー関数に置き換えます。現在:1)そのヘルパー関数が存在する場合、InterlockedIncrement64も同じ方法で(ドロップされずに)実装できます。 2)a)32ビットシステムで、b)メモリを整列させる必要がない場合、どのようにアトミックにすることができますか? –

関連する問題