最近、C#コードの一部をリファクタリングしていて、いくつかのダブルチェックされたロック方法が実行されていることに気付きました。当時はそれが悪い習慣だとは知らなかったので、本当にそれを取り除きたいと思っています。
問題は、怠惰に初期化され、多くのスレッドによって頻繁にアクセスされる必要があるクラスがあることです。また、初期化されたオブジェクトがメモリ内に長く留まらないようにするために弱参照を使用することを計画しているため、初期化を静的初期化子に移動したくありません。ただし、必要に応じて、オブジェクトを「復活」させて、これがスレッドセーフな方法で行われるようにします。
C#でReaderWriterLockSlimを使用し、最初のチェックの前にUpgradeableReadLockを入力してから、必要に応じて初期化用の書き込みロックを入力することが許容できる解決策になるかどうか疑問に思いました。これが私が念頭に置いていることです:
public class LazyInitialized
{
private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim();
private volatile WeakReference _valueReference = new WeakReference(null);
public MyType Value
{
get
{
MyType value = _valueReference.Target as MyType;
_lock.EnterUpgradeableReadLock();
try
{
if (!_valueReference.IsAlive) // needs initializing
{
_lock.EnterWriteLock();
try
{
if (!_valueReference.IsAlive) // check again
{
// prevent reading the old weak reference
Thread.MemoryBarrier();
_valueReference = new WeakReference(value = InitializeMyType());
}
}
finally
{
_lock.ExitWriteLock();
}
}
}
finally
{
_lock.ExitUpgradeableReadLock();
}
return value;
}
}
private MyType InitializeMyType()
{
// code not shown
}
}
私のポイントは、他のスレッドがアイテムをもう一度初期化しようとするべきではないということですが、値が初期化されると、多くのスレッドが同時に読み取る必要があります。アップグレード可能な読み取りロックは、書き込みロックが取得された場合にすべてのリーダーをブロックする必要があるため、オブジェクトの初期化中の動作は、アップグレード可能な読み取りロックが開始されるロックステートメントの場合と同様になります。初期化後、アップグレード可能な読み取りロックは複数のスレッドを許可するため、各スレッドを待機することによるパフォーマンスへの影響はありません。
また、ここでvolatileを使用すると、読み取り前と書き込み後にメモリバリアが自動的に挿入されるという記事を読みました。したがって、_valueReferenceオブジェクトが正しく読み取られるようにするには、読み取りと書き込みの間に手動で定義されたバリアが1つだけあれば十分だと思います。このアプローチを使用することについてのあなたのアドバイスと批判に喜んで感謝します。