Domanda

Questo mi sembra molto rumoroso. Cinque linee di sovraccarico sono semplicemente troppo.

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

Quindi come lo faresti semplicemente?

È stato utile?

Soluzione

Pensavo lo stesso, ma in 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);
    }
}

Altri suggerimenti

Tutte le soluzioni pubblicate finora sono a rischio di deadlock. Un blocco di utilizzo come questo:

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

viene convertito in qualcosa del genere:

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

Ciò significa che potrebbe verificarsi una ThreadAbortException (o simile) tra sync.Read () e il blocco try. Quando ciò accade, il blocco finally non viene mai chiamato e il blocco non viene mai rilasciato!

Per ulteriori informazioni e una migliore implementazione, consultare: Deadlock con ReaderWriterLockSlim e altri oggetti lock

Inoltre, da Blog di Joe Duffy

  

Successivamente, il blocco non è affidabile per le eccezioni asincrone come l'interruzione del thread e le condizioni di memoria insufficiente. Se uno di questi si verifica nel mezzo di uno dei metodi di blocco, lo stato di blocco può essere corrotto, causando blocchi successivi, eccezioni non gestite e (purtroppo) a causa dell'uso interno di blocchi di spin, una CPU ancorato al 100%. Quindi, se & # 8217; eseguirai il tuo codice in un ambiente che utilizza regolarmente interruzioni di thread o tenta di sopravvivere a OOM difficili, & # 8217; non sarai soddisfatto di questo blocco.

Questa non è una mia invenzione ma certamente ha reso i capelli un po 'meno grigi.

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);
    }
} 

Come usare:

using (m_lock.AcquireReadLock())
{
    // Do stuff
}

Ho finito per farlo, ma sono ancora aperto a modi migliori o difetti nel mio design.

Using m_Lock.ReadSection
    Return m_List.Count
End Using

Utilizza questo metodo / classe di estensione:

<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

Poiché il punto di un blocco è proteggere un pezzo di memoria, penso che sarebbe utile avvolgere quel ricordo in un " Locked " oggetto e renderlo accessibile solo attraverso i vari token di blocco (come menzionato da Mark ):

// 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 );
}

Ora l'unico modo per accedere all'elenco interno è chiamare l'accessor appropriato.

Funziona particolarmente bene con List<T>, poiché ha già il wrapper ReadOnlyCollection<T>. Per altri tipi, puoi sempre creare un Locked<T,T>, ma perdi la distinzione del tipo leggibile / scrivibile.

Un miglioramento potrebbe essere quello di definire i tipi R e W come involucri monouso stessi, che proteggerebbero da errori (involontari) come:

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

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

Tuttavia, ciò renderebbe Locked più complicato da usare e la versione attuale ti offre la stessa protezione che hai quando blocchi manualmente un membro condiviso.


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
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top