彼の驚くべき本C#を一言で言えば(無料の章はオンラインで入手できます)、メモリバリアが私たちに「リフレッシュ」価値を与える方法について話しています。彼の例は次のとおりです。MemoryBarrierは本当にリフレッシュ値を保証しますか?
static void Main()
{
bool complete = false;
var t = new Thread(() =>
{
bool toggle = false;
while (!complete)
{
toggle = !toggle;
}
});
t.Start();
Thread.Sleep(1000);
complete = true;
t.Join(); // Blocks indefinitely
}
リリースモードでビルドした場合は、このブロックが無期限にブロックされます。彼はこのブロックを解決するいくつかのソリューションを提供しています。 whileループでThread.MemoryBarrierを使用し、ロックを使用するか、または「完全な」揮発性静的フィールドを作成します。
私は揮発性フィールドの解決策に同意します。これは、volatileがJITのために読み込まれるレジスタではなく直接メモリの読み取りを強制するからです。しかし、私はこの最適化がフェンスと記憶の障壁とは無関係だと考えています。 JIT最適化の問題は、JITがメモリやレジスタから読み込むのが好きな場合だけです。代わりに、メモリバリアを使用するのでは、実際に、任意のメソッド呼び出しは、のようにすべてのレジスタを使用しないようにJITを「説得」:
class Program
{
[MethodImpl(MethodImplOptions.NoInlining)]
public static bool Toggle(bool toggle)
{
return !toggle;
}
static void Main()
{
bool complete = false;
var t = new Thread(() =>
{
bool toggle = false;
while (!complete)
{
toggle = Toggle(toggle);
}
});
t.Start();
Thread.Sleep(1000);
complete = true;
t.Join(); // Blocks indefinitely
}
}
ここ
私はダミーのトグルコールを作っています。生成されたアセンブリコードから、JITが "完全な"ローカル変数を読み取るためにダイレクトメモリアクセスを使用していることがはっきり分かります。したがって、私の前提は、少なくともIntel CPU上で、コンパイラの最適化を考慮すると、MemoryBarrierは「リフレッシュ」の観点から何の役割も持たない。 MemoryBarrierは完全なフェンスを注文します。それはそれです。私はそのように考えるのは正しいですか?私は揮発性などの揮発性のフィールド溶液で同意するだろう
* 2つの要件があります。まず最初に、変数をレジスタに保存することができないことをジッタに納得させることです。それはかなり簡単にやめて、メソッド呼び出しで十分です。それを宣言する* volatile *はx86上で有効なスレッジハンマーです。または、ロックや連動のようなコードの同期に敏感な部分からの自然な方法。 Interlocked.Equals(toggle、false)を挿入するだけで十分です。もう1つは、バリアから取得するメモリアクセス順序です.Volalite.Read/Writeが最も効果的です。ジッタが変数volatileを扱うように強制しますので、1つで十分です –
私はあなたに同意します:これはメモリバリアとは関係ありません。 –
@HansPassant、 'volatile'は' Volatile.Read'/'Volatile.Write'と同じくらい有効なアプローチですが、あなたはそれらを使うのを忘れることはできません。また、 'Interlocked.Equals'は静的な' Object.Equals'だけで、同期に関連するものでは十分ではありません。 – acelent