Снова дважды проверенная блокировка и C #

Недавно я занимался рефакторингом некоторого кода C # и обнаружил, что есть несколько перепроверенных практик блокировки. Тогда я не знал, что это плохая практика, и я действительно хочу избавиться от нее.

Проблема в том, что у меня есть класс, который нужно лениво инициализировать и к которому часто обращаются многие потоки. Я также не хочу переносить инициализацию в статический инициализатор, потому что я планирую использовать слабую ссылку, чтобы инициализированный объект не оставался в памяти слишком долго. Однако при необходимости я хочу «оживить» объект, гарантируя, что это произойдет в потокобезопасном режиме.

Мне было интересно, использовать ли ReaderWriterLockSlim в C # и вводить 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    
    }
}

Я хочу сказать, что ни один другой поток не должен пытаться инициализировать элемент еще раз, в то время как многие потоки должны читать одновременно после инициализации значения. Обновляемая блокировка чтения должна блокировать всех считывателей, если была получена блокировка записи, поэтому во время инициализации объекта поведение будет аналогично наличию оператора блокировки, в котором начинается обновляемая блокировка чтения. После инициализации обновляемая блокировка чтения разрешит несколько потоков, поэтому снижение производительности ожидания каждого потока не будет.

Я также прочитал статью здесь , в которой говорится, что энергозависимость вызывает автоматическую вставку барьеров памяти перед чтения и после записи, поэтому я предполагаю, что только одного определенного вручную барьера между чтением и записью будет достаточно, чтобы гарантировать правильное чтение объекта _valueReference. Я буду рад вашим советам и критике за использование этого подхода.

5
задан Juliet 13 June 2011 в 19:03
поделиться