2

この MSDN マガジンの記事に関連する質問があります。

はじめに 先ほど説明したように、コンパイラは複数の読み取りを 1 つに融合することがあります。コンパイラは、1 つの読み取りを複数の読み取りに分割することもできます。.NET Framework 4.5 では、読み取りの導入は読み取りの削除よりもはるかに一般的ではなく、非常にまれな特定の状況でのみ発生します。ただし、時々発生します。

public class ReadIntro {
  private Object _obj = new Object();
  void PrintObj() {
    Object obj = _obj;
    if (obj != null) {
      Console.WriteLine(obj.ToString());
    // May throw a NullReferenceException
    }
  }
  void Uninitialize() {
    _obj = null;
  }
}

PrintObj メソッドを調べると、obj.ToString 式で obj 値が null になることはないようです。ただし、そのコード行は実際には NullReferenceException をスローする可能性があります。CLR JIT は、次のように記述されているかのように PrintObj メソッドをコンパイルする場合があります。

void PrintObj() {
  if (_obj != null) {
    Console.WriteLine(_obj.ToString());
  }
}

しかし、それはイベントを扱うパターンではありませんか?!

void RaiseEvent()
{
    var myEvent = MyEvent;
    if (myEvent != null)
    {
         myEvent(this, EventArgs.Empty);
    }
}

ここで重要なことを見逃していますか?

4

1 に答える 1

5

この記事も私を混乱させ、いくつかの調査を行いました。私は 2 つの学派を見つけました。

1.パターンは安全だと言う人もいます

CLR 2.0 メモリ モデルは 1.x よりも厳密であり、禁止されているためです。

「読み取りと書き込みは導入できません」、MSDN マガジン (2005 年 10 月)、記事マルチスレッド アプリにおけるローロック技術の影響を理解する.

「.NET メモリ モデルでは、GC ヒープ メモリを参照する通常の変数については禁止されています [概要を読む]」、Joe Duffy 著、Concurrent Programming on Windows、pp517-8。

[注: Joe Duffy は基本的に同じことを言っていますが、共有されていないため安全なスタックに紹介を読む可能性を残しています]

これらの「.NET 2.0 メモリ モデル」の説明は奇妙だと思います。2012 ECMA CLI 仕様と C# 標準を読みましたが、導入を禁止するステートメントへの参照は見つかりませんでした。2.0 から 4 の間でメモリ モデルが弱められた可能性は低いです。(??)

一方、JIT チームはそれらのパターンを認識しており、少なくとも x86 ではそれらを壊すことはないと思いますが、これを言うことは、それが標準にあると言うのと同じではありません。チームの決定は、将来または他のプラットフォームで変更される可能性があります。

EDIT以下のEric Lippert のコメントをお見逃しなく。ECMA 標準にはそれについて何も記載されておらず、他の実装 (Mono など) を使用する場合、すべての賭けは無効になります。編集終了

2. 安全ではないと言う人もいます

具体的には、引用した記事の Igor Ostrovsky と、このブログ投稿のコメント内のディスカッションでの Stephen Toub です。協調的に一時停止する async-methods.aspx

基本的に、読み取りの導入または削除は、シングルスレッドの動作を変更しない場合、C# と JIT の両方で実行できる一般的なコンパイラの最適化であると言われています。

[注: Eric Lippert は、現時点では C# コンパイラはそのような最適化を行っていないと述べています。]

Igor は、JIT が非常に保守的であることを認識しているようであり、サンプル コードがx86-x64 上の .NET 4.5 で壊れないことを記事で明示的に指摘していることに注意してください。一方で彼は、より複雑なコード パターン、将来または過去の .net リリース、または他のプラットフォームであるかどうかを正確に説明することなく、他のケースでは壊れる可能性があると述べています。

解決

100% 安全にしたい場合の解決策は、揮発性読み取りを使用することです。揮発性の読み取りと書き込みは、C# 標準によって副作用として定義されているため、導入も削除もできません。

ECMA CLI 標準には、揮発性の読み取りと書き込みを削除しないことについて、同様の明示的なステートメントがあります。

スレッドセーフなイベントに関する注意

多くの人が指摘しているように、スレッドセーフにはイベントを発生させるコードだけではありません。イベント ハンドラーは、サブスクライブ解除後に呼び出される準備ができている必要があります。

最良のガイダンスは「やらないこと」であるという Hans Passant の意見に同意しますが、そうする必要がある場合もあります。そのような場合は、イベント ハンドラー コードもスレッド セーフであることを確認してください。そのような場合は、より単純なロックベースの同期アプローチを検討することもできます。

于 2013-05-28T10:26:49.093 に答える