2008-10-04 9 views

ответ

16

я думал то же самое, но в 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); 
    } 
} 
+0

+1 отличный сниппет! – 2012-03-20 20:01:29

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 так что двойной Dispose() не вызывает проблем (маловероятно, но ...) второй - нет необходимости абонент знать о ReadWrapper если IDisposable было бы достаточно. Но мне это нравится; -p – 2008-10-04 09:31:41

+0

Хорошая точка зрения, я не хотел бы разоблачать тип ReadWrapper. – 2008-10-04 09:36:30

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() и блок Try. Когда это произойдет, блок finally никогда не будет вызван, и блокировка никогда не будет выпущена!

Для получения дополнительной информации, а также более эффективной реализации см: Deadlock with ReaderWriterLockSlim and other lock objects

Кроме того, из Joe Duffy's Blog

Далее, замок не устойчив к асинхронным исключениям, таким как поток прерывается и из условий памяти. Если одно из них встречается в середине одного из методов блокировки, состояние блокировки может быть повреждено, что приводит к последующим взаимоблокировкам, необработанным исключениям и (к сожалению) из-за использования встроенных спиновых замков, привязанного к 100% CPU. Поэтому, если вы собираетесь запускать свой код в среде, которая регулярно использует прерывания потока или пытается выжить в жестких OOM, вы не будете довольны этой блокировкой.

 Смежные вопросы

  • Нет связанных вопросов^_^