私はこれについて数時間熟考してきましたが、その難しさに少し驚いています。複数のスレッドからアクセスされる基本クラスから、特定のオブジェクトのリフレクション データの静的キャッシュを初期化しようとしています。キャッシュを初期化するための正しいパターンを思いつくのに苦労しています。
私が最初に考えたのは、静的キャッシュを 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 (または同様のもの) を使用して想定できる、キャッシュ アクセスをスレッドセーフにする方法に関するものではないことに注意してください。