سؤال

هذا يبدو صاخبة جدا بالنسبة لي. خمسة أسطر من النفقات العامة أكثر من اللازم.

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

فكيف يمكنك ببساطة هذا؟

هل كانت مفيدة؟

المحلول

كنت أفكر في نفس الشيء ، لكن في 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);
    }
}

نصائح أخرى

جميع الحلول المنشورة حتى الآن معرضة لخطر الجمود. استخدام كتلة مثل هذا:

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 () وكتلة المحاولة. عندما يحدث هذا ، لم يتم استدعاء الكتلة أخيرًا ، ولم يتم إطلاق القفل أبدًا!

لمزيد من المعلومات ، ونظر تنفيذ أفضل:deadlock مع readerWriterlockslim وغيرها من كائنات القفل

أيضا ، من مدونة جو دوفي

بعد ذلك ، لا يعد القفل قويًا للاستثناءات غير المتزامنة مثل إحباط مؤشرات الترابط وخارج ظروف الذاكرة. إذا حدث أحد هذه الأشياء أثناء وجوده في وسط إحدى طرق القفل ، يمكن أن تكون حالة القفل تالفًا ، مما تسبب في حالات الجمود اللاحقة ، والاستثناءات غير المعروفة ، و (للأسف) بسبب استخدام أقفال الدوران داخليًا ، وهي وحدة المعالجة المركزية بنسبة 100 ٪. لذا ، إذا كنت ستقوم بتشغيل التعليمات البرمجية الخاصة بك في بيئة تستخدم بانتظام إحباط مؤشرات الترابط أو محاولات للبقاء على قيد الحياة ، فلن تكون سعيدًا بهذا القفل.

هذا ليس اختراعي ولكنه بالتأكيد صنعه الشعر أقل رماديًا.

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
}

انتهى بي الأمر إلى القيام بذلك ، لكنني ما زلت منفتحًا على طرق أو عيوب أفضل في تصميمي.

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

نظرًا لأن نقطة القفل تتمثل في حماية بعض الذاكرة ، أعتقد أنه سيكون من المفيد لف تلك الذاكرة في كائن "مغلق" ، وجعلها متوسطة فقط من خلال مختلف الرموز القفل (كما ذكرها علامة):

// 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
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top