2008-10-04 12 views
11

यह मेरे लिए बहुत शोर लगता है। ओवरहेड की पांच रेखाएं बहुत अधिक हैं।रीडरवाइटर लॉक में प्रवेश करने और निकालने में आप कैसे सरल बनायेंगे?

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

तो आप यह कैसे करेंगे?

उत्तर

16

मैं यही सोच रहा था, लेकिन सी # ;-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); 
    } 
} 
+0

+1 महान स्निपेट! –

+1

मजेदार, मैं इस बारे में भूल गया था और बस मौके से यह मेरी प्रतिष्ठा के अंक में खुलता है जैसे कि मैं इससे लाभ उठा सकता हूं। –

0

मैंने ऐसा करने का अंत किया, लेकिन मैं अभी भी अपने डिजाइन में बेहतर तरीके या त्रुटियों के लिए खुला हूं।

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 
+1

दो विचार: पहले, आप m_Lock स्पष्ट करना चाहिए ताकि एक डबल निपटान() मुद्दों का कारण नहीं है (संभावना नहीं है, लेकिन ...) दूसरा - कोई जरूरत फोन करने वाले ReadWrapper बारे में पता करने के लिए वहाँ है अगर IDisposable पर्याप्त होगा लेकिन मुझे यह पसंद है ;- –

+0

अच्छा बिंदु, मैं रीडवापर प्रकार को किसी भी तरह से बेनकाब नहीं करना चाहता था। –

0

एक ताला के बिंदु के बाद से में रक्षा के लिए है स्मृति का कुछ टुकड़ा, मुझे लगता है कि उस स्मृति को "लॉक" ऑब्जेक्ट में लपेटना उपयोगी होगा, और केवल इसे विभिन्न लॉक टोकन के माध्यम से एक्सेस करें (जैसा कि 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); 
} 

अब आंतरिक सूची का उपयोग करने के लिए एक ही रास्ता उचित एक्सेसर फोन करके है।

यह List<T> के लिए विशेष रूप से अच्छी तरह से काम करता है, क्योंकि इसमें पहले से ही ReadOnlyCollection<T> रैपर है। अन्य प्रकार के लिए, आप हमेशा Locked<T,T> बना सकते हैं, लेकिन फिर आप अच्छे पठनीय/लिखने योग्य प्रकार भेद पर हार जाते हैं।

ही एक सुधार के लिए खुद को डिस्पोजेबल रैपर के रूप में R और W प्रकार, जो (inadvertant) त्रुटियों के खिलाफ की तरह संरक्षित हैं परिभाषित करने के लिए हो सकता है:

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 
} 
2

यह मेरा आविष्कार नहीं है, लेकिन यह निश्चित रूप से थोड़ा कम भूरे बालों से बना दिया है।

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 
} 
3

सभी समाधान अब तक तैनात गतिरोध का खतरा होता है।इस तरह एक का उपयोग कर ब्लॉक:

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 with ReaderWriterLockSlim and other lock objects

इसके अलावा, Joe Duffy's Blog

अगला से, ताला जैसे धागा aborts और स्मृति की स्थिति से बाहर अतुल्यकालिक अपवाद को मजबूत नहीं है। यदि इनमें से एक लॉक के तरीकों में से एक के बीच में होता है, तो लॉक स्थिति भ्रष्ट हो सकती है, जिससे बाद में डेडलॉक्स, अनचाहे अपवाद, और (दुख की बात है) आंतरिक रूप से स्पिन ताले के उपयोग के कारण, एक 100% सीपीयू चिपकाया जाता है। तो यदि आप अपने कोड को ऐसे माहौल में चलाने जा रहे हैं जो नियमित रूप से थ्रेड एबॉर्ट्स या हार्ड ओओएम से बचने के प्रयासों का उपयोग करता है, तो आप इस लॉक से खुश नहीं होंगे।

+0

अगर कोई थ्रेडएबॉर्ट अपवादों के आसपास फेंक रहा है तो केवल डेडलॉक की तुलना में कहीं अधिक गंभीर समस्याएं हैं। फिर केवल एक थ्रेडएबॉर्ट अपवाद उचित है जब इसे थ्रेड द्वारा उठाया जाता है जैसे कि HttpResponse.End को कॉल करते समय। –

+0

मुझे लगता है कि यह एक महान बिंदु है और अधिक ध्यान देना चाहिए। जब तक मैं इसे पढ़ नहीं लेता तब तक मैं वास्तव में मार्क ग्रेवेल के जवाब को आकर्षित करता था। – Pandincus

+1

... दोनों लिंक मर गए हैं। – Beachwalker