Как бы вы упростили вход и выход из ReaderWriterLock?

Мне это кажется очень шумным. Пять строк над головой - это слишком много.

m_Lock.EnterReadLock()
Try
    Return m_List.Count
Finally
    m_Lock.ExitReadLock()
End Try

Так как бы вы могли это просто сделать?

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
11
0
5 621
5
Перейти к ответу Данный вопрос помечен как решенный

Ответы 5

В итоге я сделал это, но я все еще открыт для лучших способов или недостатков в моем дизайне.

Using m_Lock.ReadSection
    Return m_List.Count
End Using

Здесь используется этот метод / класс расширения:

<Extension()> Public Function ReadSection(ByVal lock As ReaderWriterLockSlim) As ReadWrapper
    Return New ReadWrapper(lock)
End Function


Public NotInheritable Class ReadWrapper
    Implements IDisposable

    Private m_Lock As ReaderWriterLockSlim
    Public Sub New(ByVal lock As ReaderWriterLockSlim)
        m_Lock = lock
        m_Lock.EnterReadLock()
    End Sub
    Public Sub Dispose() Implements IDisposable.Dispose
        m_Lock.ExitReadLock()
    End Sub

End Class

Две мысли: во-первых, вы должны очистить m_Lock, чтобы двойной Dispose () не вызывал проблем (маловероятно, но ...), во-вторых, вызывающему абоненту не нужно знать о ReadWrapper, если IDisposable будет достаточно. Но мне это нравится ;-p

Marc Gravell 04.10.2008 13:31

Хороший момент, я все равно не хотел раскрывать тип ReadWrapper.

Jonathan Allen 04.10.2008 13:36
Ответ принят как подходящий

Я думал то же самое, но на C# ;-p

using System;
using System.Threading;

class Program
{
    static void Main()
    {
        ReaderWriterLockSlim sync = new ReaderWriterLockSlim();

        using (sync.Read())
        {
           // etc    
        }
    }


}
public static class ReaderWriterExt
{
    sealed class ReadLockToken : IDisposable
    {
        private ReaderWriterLockSlim sync;
        public ReadLockToken(ReaderWriterLockSlim sync)
        {
            this.sync = sync;
            sync.EnterReadLock();
        }
        public void Dispose()
        {
            if (sync != null)
            {
                sync.ExitReadLock();
                sync = null;
            }
        }
    }
    public static IDisposable Read(this ReaderWriterLockSlim obj)
    {
        return new ReadLockToken(obj);
    }
}

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

Jonathan Allen 14.03.2016 19:28

что насчет написания?

kofifus 20.07.2018 06:55

@kofifus то же самое с EnterWriteLock?

Marc Gravell 20.07.2018 09:54

Есть ли причина не использовать здесь структуры для токена @MarcGravell?

Yair Halberstadt 29.07.2019 13:35

@YairHalberstadt бит изменчивости - это изменение; лично я бы использую здесь структуру, вероятно, структуру только для чтения, и если кто-то дважды вызывает dispose: это их проблема в том, что она используется неправильно, но ... это изменение

Marc Gravell 29.07.2019 16:10

Поскольку целью блокировки является защита некоторой части памяти, я думаю, было бы полезно обернуть эту память в объект "Locked" и сделать его доступным только через различные токены блокировки (как указано в отметка):

// Stores a private List<T>, only accessible through lock tokens
//  returned by Read, Write, and UpgradableRead.
var lockedList = new LockedList<T>( );
using( var r = lockedList.Read( ) ) {
  foreach( T item in r.Reader )
    ...
}
using( var w = lockedList.Write( ) ) {
  w.Writer.Add( new T( ) );
}
T t = ...;
using( var u = lockedList.UpgradableRead( ) ) {
  if ( !u.Reader.Contains( t ) )
    using( var w = u.Upgrade( ) )
      w.Writer.Add( t );
}

Теперь единственный способ получить доступ к внутреннему списку - вызвать соответствующий метод доступа.

Это особенно хорошо работает для List<T>, поскольку он уже имеет оболочку ReadOnlyCollection<T>. Для других типов вы всегда можете создать Locked<T,T>, но тогда вы потеряете хорошее различие между читаемыми и записываемыми типами.

Одним из улучшений может быть определение типов R и W как самих одноразовых оболочек, которые будут защищены от (непреднамеренных) ошибок, таких как:

List<T> list;
using( var w = lockedList.Write( ) )
  list = w.Writable;

//BAD: "locked" object leaked outside of lock scope
list.MakeChangesWithoutHoldingLock( );

Однако это сделало бы использование Locked более сложным, а текущая версия обеспечивает такую ​​же защиту, как и при ручной блокировке разделяемого члена.


sealed class LockedList<T> : Locked<List<T>, ReadOnlyCollection<T>> {
  public LockedList( )
    : base( new List<T>( ), list => list.AsReadOnly( ) )
  { }
}

public class Locked<W, R> where W : class where R : class {
  private readonly LockerState state_;
  public Locked( W writer, R reader ) { this.state_ = new LockerState( reader, writer ); }
  public Locked( W writer, Func<W, R> getReader ) : this( writer, getReader( writer ) ) { }

  public IReadable Read( ) { return new Readable( this.state_ ); }
  public IWritable Write( ) { return new Writable( this.state_ ); }
  public IUpgradable UpgradableRead( ) { return new Upgradable( this.state_ ); }


  public interface IReadable : IDisposable { R Reader { get; } }
  public interface IWritable : IDisposable { W Writer { get; } }
  public interface IUpgradable : IReadable { IWritable Upgrade( );}


  #region Private Implementation Details
  sealed class LockerState {
    public readonly R Reader;
    public readonly W Writer;
    public readonly ReaderWriterLockSlim Sync;

    public LockerState( R reader, W writer ) {
      Debug.Assert( reader != null && writer != null );
      this.Reader = reader;
      this.Writer = writer;
      this.Sync = new ReaderWriterLockSlim( );
    }
  }

  abstract class Accessor : IDisposable {
    private LockerState state_;
    protected LockerState State { get { return this.state_; } }
    protected Accessor( LockerState state ) {
      Debug.Assert( state != null );
      this.Acquire( state.Sync );
      this.state_ = state;
    }

    protected abstract void Acquire( ReaderWriterLockSlim sync );
    protected abstract void Release( ReaderWriterLockSlim sync );

    public void Dispose( ) {
      if ( this.state_ != null ) {
        var sync = this.state_.Sync;
        this.state_ = null;
        this.Release( sync );
      }
    }
  }

  class Readable : Accessor, IReadable {
    public Readable( LockerState state ) : base( state ) { }
    public R Reader { get { return this.State.Reader; } }
    protected override void Acquire( ReaderWriterLockSlim sync ) { sync.EnterReadLock( ); }
    protected override void Release( ReaderWriterLockSlim sync ) { sync.ExitReadLock( ); }
  }

  sealed class Writable : Accessor, IWritable {
    public Writable( LockerState state ) : base( state ) { }
    public W Writer { get { return this.State.Writer; } }
    protected override void Acquire( ReaderWriterLockSlim sync ) { sync.EnterWriteLock( ); }
    protected override void Release( ReaderWriterLockSlim sync ) { sync.ExitWriteLock( ); }
  }

  sealed class Upgradable : Readable, IUpgradable {
    public Upgradable( LockerState state ) : base( state ) { }
    public IWritable Upgrade( ) { return new Writable( this.State ); }
    protected override void Acquire( ReaderWriterLockSlim sync ) { sync.EnterUpgradeableReadLock( ); }
    protected override void Release( ReaderWriterLockSlim sync ) { sync.ExitUpgradeableReadLock( ); }
  }
  #endregion
}

Это не мое изобретение, но оно определенно сделало волосы менее седыми.

internal static class ReaderWriteLockExtensions
{
    private struct Disposable : IDisposable
    {
        private readonly Action m_action;
        private Sentinel m_sentinel;

        public Disposable(Action action)
        {
            m_action = action;
            m_sentinel = new Sentinel();
        }

        public void Dispose()
        {
            m_action();
            GC.SuppressFinalize(m_sentinel);
        }
    }

    private class Sentinel
    {
        ~Sentinel()
        {
            throw new InvalidOperationException("Lock not properly disposed.");
        }
    }

    public static IDisposable AcquireReadLock(this ReaderWriterLockSlim lock)
    {
        lock.EnterReadLock();
        return new Disposable(lock.ExitReadLock);
    }

    public static IDisposable AcquireUpgradableReadLock(this ReaderWriterLockSlim lock)
    {
        lock.EnterUpgradeableReadLock();
        return new Disposable(lock.ExitUpgradeableReadLock);
    }

    public static IDisposable AcquireWriteLock(this ReaderWriterLockSlim lock)
    {
        lock.EnterWriteLock();
        return new Disposable(lock.ExitWriteLock);
    }
} 

Как использовать:

using (m_lock.AcquireReadLock())
{
    // Do stuff
}
docs.microsoft.com/en-us/visualstudio/code-quality/ca1065 Throwing an exception from a finalizer causes the CLR to fail fast, which tears down the process. Therefore, throwing exceptions in a finalizer should always be avoided.
Wouter 21.10.2019 22:47

Что ж, в таком случае проявляется более серьезный недостаток дизайна, поэтому предпочтительнее быстро выйти из строя.

Patrik Svensson 23.10.2019 00:57

Все опубликованные решения могут зайти в тупик. Такой блок using:

ReaderWriterLockSlim sync = new ReaderWriterLockSlim();
using (sync.Read())
{
  // Do stuff
}

преобразуется во что-то вроде этого:

ReaderWriterLockSlim sync = new ReaderWriterLockSlim();
IDisposable d = sync.Read();
try
{
  // Do stuff
}
finally
{
  d.Dispose();
}

Это означает, что исключение ThreadAbortException (или подобное) могло произойти между sync.Read () и блоком try. Когда это происходит, блок finally никогда не вызывается, и блокировка никогда не снимается!

Для получения дополнительной информации и лучшей реализации см .: Тупик с ReaderWriterLockSlim и другими объектами блокировки. Короче говоря, лучшая реализация сводится к перемещению блокировки в блок try следующим образом:

ReaderWriterLockSlim myLock = new ReaderWriterLockSlim();
try
{
    myLock.EnterReadLock();
    // Do stuff
}
finally
{
    // Release the lock
    myLock.ExitReadLock();
}

Обертка класс, подобная той, которая содержится в принятом ответе, будет выглядеть так:

  /// <summary>
  /// Manager for a lock object that acquires and releases the lock in a manner
  /// that avoids the common problem of deadlock within the using block
  /// initialisation.
  /// </summary>
  /// <remarks>
  /// This manager object is, by design, not itself thread-safe.
  /// </remarks>
  public sealed class ReaderWriterLockMgr : IDisposable
  {
    /// <summary>
    /// Local reference to the lock object managed
    /// </summary>
    private ReaderWriterLockSlim _readerWriterLock = null;

    private enum LockTypes { None, Read, Write, Upgradeable }
    /// <summary>
    /// The type of lock acquired by this manager
    /// </summary>
    private LockTypes _enteredLockType = LockTypes.None;

    /// <summary>
    /// Manager object construction that does not acquire any lock
    /// </summary>
    /// <param name = "ReaderWriterLock">The lock object to manage</param>
    public ReaderWriterLockMgr(ReaderWriterLockSlim ReaderWriterLock)
    {
      if (ReaderWriterLock == null)
        throw new ArgumentNullException("ReaderWriterLock");
      _readerWriterLock = ReaderWriterLock;
    }

    /// <summary>
    /// Call EnterReadLock on the managed lock
    /// </summary>
    public void EnterReadLock()
    {
      if (_readerWriterLock == null)
        throw new ObjectDisposedException(GetType().FullName);
      if (_enteredLockType != LockTypes.None)
        throw new InvalidOperationException("Create a new ReaderWriterLockMgr for each state you wish to enter");
      // Allow exceptions by the Enter* call to propogate
      // and prevent updating of _enteredLockType
      _readerWriterLock.EnterReadLock();
      _enteredLockType = LockTypes.Read;
    }

    /// <summary>
    /// Call EnterWriteLock on the managed lock
    /// </summary>
    public void EnterWriteLock()
    {
      if (_readerWriterLock == null)
        throw new ObjectDisposedException(GetType().FullName);
      if (_enteredLockType != LockTypes.None)
        throw new InvalidOperationException("Create a new ReaderWriterLockMgr for each state you wish to enter");
      // Allow exceptions by the Enter* call to propogate
      // and prevent updating of _enteredLockType
      _readerWriterLock.EnterWriteLock();
      _enteredLockType = LockTypes.Write;
    }

    /// <summary>
    /// Call EnterUpgradeableReadLock on the managed lock
    /// </summary>
    public void EnterUpgradeableReadLock()
    {
      if (_readerWriterLock == null)
        throw new ObjectDisposedException(GetType().FullName);
      if (_enteredLockType != LockTypes.None)
        throw new InvalidOperationException("Create a new ReaderWriterLockMgr for each state you wish to enter");
      // Allow exceptions by the Enter* call to propogate
      // and prevent updating of _enteredLockType
      _readerWriterLock.EnterUpgradeableReadLock();
      _enteredLockType = LockTypes.Upgradeable;
    }

    /// <summary>
    /// Exit the lock, allowing re-entry later on whilst this manager is in scope
    /// </summary>
    /// <returns>Whether the lock was previously held</returns>
    public bool ExitLock()
    {
      switch (_enteredLockType)
      {
        case LockTypes.Read:
          _readerWriterLock.ExitReadLock();
          _enteredLockType = LockTypes.None;
          return true;
        case LockTypes.Write:
          _readerWriterLock.ExitWriteLock();
          _enteredLockType = LockTypes.None;
          return true;
        case LockTypes.Upgradeable:
          _readerWriterLock.ExitUpgradeableReadLock();
          _enteredLockType = LockTypes.None;
          return true;
      }
      return false;
    }

    /// <summary>
    /// Dispose of the lock manager, releasing any lock held
    /// </summary>
    public void Dispose()
    {
      if (_readerWriterLock != null)
      {
        ExitLock();
        // Tidy up managed resources
        // Release reference to the lock so that it gets garbage collected
        // when there are no more references to it
        _readerWriterLock = null;
        // Call GC.SupressFinalize to take this object off the finalization
        // queue and prevent finalization code for this object from
        // executing a second time.
        GC.SuppressFinalize(this);
      }
    }

    protected ~ReaderWriterLockMgr()
    {
      if (_readerWriterLock != null)
        ExitLock();
      // Leave references to managed resources so that the garbage collector can follow them
    }
  }

Использование следующим образом:

ReaderWriterLockSlim myLock = new ReaderWriterLockSlim();

using (ReaderWriterLockMgr lockMgr = new ReaderWriterLockMgr(myLock))
{
    lockMgr.EnterReadLock();
    // Do stuff
}

Также из Блог Джо Даффи

Next, the lock is not robust to asynchronous exceptions such as thread aborts and out of memory conditions. If one of these occurs while in the middle of one of the lock’s methods, the lock state can be corrupt, causing subsequent deadlocks, unhandled exceptions, and (sadly) due to the use of spin locks internally, a pegged 100% CPU. So if you’re going to be running your code in an environment that regularly uses thread aborts or attempts to survive hard OOMs, you’re not going to be happy with this lock.

Если кто-то бросает ThreadAbortExceptions, то есть гораздо более серьезные проблемы, чем просто тупик. Тогда ТОЛЬКО время, когда ThreadAbortException является подходящим, - это когда оно вызывается самим потоком, например, при вызове HttpResponse.End.

Jonathan Allen 05.08.2010 06:42

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

Pandincus 11.10.2012 21:36

... обе ссылки мертвы.

Beachwalker 10.08.2016 16:53

"... и блокировка никогда не снимается!" но разве сборщик мусора не вызовет его несколько позже, потому что d больше не упоминается? Так что в большинстве случаев «проблема» не так уж и важна. Просто догадка...

Beachwalker 10.08.2016 16:55

@Beachwalker GC не вызывает Dispose, но у вас может быть деструктор в классе, который вызывает Dispose: stackoverflow.com/q/1691846/681538

Alex 19.07.2018 17:50

Правильно, в моем заявлении это было недостаточно ясно указано. Я предположил, что у класса есть вызов в деструкторе и он правильно реализован, как и должно быть. Итак, GC тогда неявно вызывает это.

Beachwalker 02.08.2018 12:21

Другие вопросы по теме