3

私はこれについて数時間熟考してきましたが、その難しさに少し驚いています。複数のスレッドからアクセスされる基本クラスから、特定のオブジェクトのリフレクション データの静的キャッシュを初期化しようとしています。キャッシュを初期化するための正しいパターンを思いつくのに苦労しています。

私が最初に考えたのは、静的キャッシュを null に初期化し、コンストラクターで null かどうかを確認し、そうでない場合はビルドして設定するということでした。すなわち:

class TestBase
{
  private static ConcurrentDictionary<string, PropertyInfo> Cache;

  protected TestBase()
  {
    if(Cache == null)
    {
      ConcurrentDictionary<string, PropertyInfo> cache =
        new ConcurrentDictionary<string, PropertyInfo>();
      // Populate...
      Cache = cache;
    }
  }
}

これには、最初のオブジェクトがまだキャッシュにデータを格納している間に別のオブジェクトを作成すると、最終的に 2 つのキャッシュが作成され、2 番目のオブジェクトが最初のオブジェクトを上書きしてしまうという欠点があります。両方とも完全なキャッシュになるので、これはおそらく問題ありませんが、ハックなようで、もっとうまくやれることを願っています。

したがって、私の 2 番目の考えは、インスタンスが作成される前に AppDomain ごとに 1 回だけ呼び出される静的コンストラクターでキャッシュを初期化することでした。StackOverflow には、この方向性を示す同様の質問に対する回答がいくつかあるようです。静的コンストラクターが派生型のリフレクション データにアクセスできないことに気付くまで、これは素晴らしいことのように思えました。

コンストラクターで常にアクセスを同期して、1 つのスレッドのみがキャッシュを作成/設定し、それが発生している間は他のアクセスがブロックされるようにすることができますが、一度だけ発生する操作を保護するためだけに、すべての構築をロックしています。それがパフォーマンスに与える影響は好きではありません。

私が今持っているのは、Interlocked.Exchange と ManualResetEventSlim を使用して設定されたフラグです。次のようになります。

class TestBase
{
  private static ConcurrentDictionary<string, PropertyInfo> Cache;
  private static volatile int BuildingCache = 0;
  private static ManualResetEventSlim CacheBuilt =
    new ManualResetEventSlim();

  protected TestBase()
  {
    if(Interlocked.Exchange(ref BuildingCache, 1) == 0)
    {
      Cache = new ConcurrentDictionary<string, PropertyInfo>();
      // Populate...
      CacheBuilt.Set();
    }
    CacheBuilt.Wait();
  }
}

この種のことを行う方法として、すでに受け入れられている、または少なくとも知られている方法があるのではないかと思いますが、これですか? そうでない場合、キャッシュの初期化を同期するより良い方法はありますか? 問題は、ConcurrentDictionary (または同様のもの) を使用して想定できる、キャッシュ アクセスをスレッドセーフにする方法に関するものではないことに注意してください。

4

2 に答える 2

3

このクラスを使用するとLazy<T>、独自の配管を作成せずに、スレッドセーフな方法で何かを遅延初期化できます。

リフレクションデータを熱心にキャッシュしたい場合はAssembly.GetTypes()、互換性のあるタイプ(たとえば、特定の属性で装飾されているタイプ)をスキャンするために使用する必要があります。例えば:

var types = typeof(TestBase).Assembly.GetTypes().Where(type => --some condition--);
于 2012-04-30T17:01:47.933 に答える
0

コンストラクター ( ) の外で辞書を作成しprivate static ConcurrentDictionary<string, PropertyInfo> Cache = new ConcurrentDictionary<string, PropertyInfo>();、Count をチェックして、キャッシュを構築する必要があるかどうかを判断する必要があります。次に、TryAddキャッシュを構築するときに追加操作を実行するために使用する場合、独自のスレッド ロックを行う必要はまったくありません。

別のプロパティ IsCahceValid を使用して、キャッシュが完了したことを示すだけで、コメントで提起した問題を処理できます。TryAdd を使用しているので、変数をロックする必要さえありません。これは、最初の完了が正常に機能した時点で変数を true に設定するためです。

于 2012-04-30T16:58:29.853 に答える