2008-10-04 32 views

Trả lời

16

Tôi đã suy nghĩ giống nhau, nhưng trong 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 đoạn trích tuyệt vời! –

+1

Hài hước, tôi đã từ lâu quên mất điều này và chỉ tình cờ nó nổi lên trong danh tiếng của tôi chỉ lên khi tôi có thể hưởng lợi từ nó. –

0

Tôi đã kết thúc việc này, nhưng tôi vẫn mở ra những cách hay sai sót tốt hơn trong thiết kế của mình.

Using m_Lock.ReadSection 
    Return m_List.Count 
End Using 

này sử dụng phương pháp mở rộng này/lớp:

<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

Hai suy nghĩ: đầu tiên, bạn nên xóa m_Lock sao cho bỏ hai lần() không gây ra vấn đề (không, nhưng ...) giây - không cần người gọi biết về ReadWrapper nếu IDisposable sẽ đủ. Nhưng tôi thích nó ;-p –

+0

Điểm tốt, tôi không muốn để lộ các loại ReadWrapper anyways. –

0

Kể từ thời điểm một khóa là để bảo vệ một số bộ nhớ, tôi nghĩ rằng nó sẽ hữu ích để bọc bộ nhớ đó trong một đối tượng "Đã khóa", và chỉ làm cho nó có thể truy cập thông qua các thẻ khóa khác nhau (như đã đề cập bởi 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); 
} 

Bây giờ cách duy nhất để truy cập vào danh sách nội bộ là bằng cách gọi accessor thích hợp.

Điều này hoạt động đặc biệt tốt cho List<T>, vì nó đã có bao bì ReadOnlyCollection<T>. Đối với các loại khác, bạn luôn có thể tạo ra một Locked<T,T>, nhưng sau đó bạn bị mất trên sự phân biệt loại dễ đọc/có thể ghi.

Một cải tiến có thể là để xác định các loại RW như giấy gói dùng một lần bản thân, mà sẽ được bảo vệ chống lại các lỗi (inadvertant) như:

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

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

Tuy nhiên, điều này sẽ làm cho Locked phức tạp hơn để sử dụng, và Phiên bản hiện tại cung cấp cho bạn sự bảo vệ giống như bạn có khi khóa thủ công một thành viên được chia sẻ.


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

Đây không phải là phát minh của tôi, nhưng chắc chắn nó đã được thực hiện bởi tóc một chút ít màu xám.

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

Làm thế nào để sử dụng:

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

Tất cả các giải pháp được đăng cho đến nay có nguy cơ bị bế tắc. Một sử dụng khối như thế này:

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

được chuyển đổi thành một cái gì đó như thế này:

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

Điều này có nghĩa rằng một ThreadAbortException (hoặc tương tự) có thể xảy ra giữa sync.Read() và các khối try. Khi điều này xảy ra, khối cuối cùng không bao giờ được gọi, và khóa không bao giờ được giải phóng!

Để biết thêm thông tin, và thực hiện tốt hơn xem: Deadlock with ReaderWriterLockSlim and other lock objects

Ngoài ra, từ Joe Duffy's Blog

Tiếp theo, các khóa không phải là mạnh mẽ để trường hợp ngoại lệ không đồng bộ như chủ đề hủy bỏ và ra khỏi tình trạng bộ nhớ. Nếu một trong những điều này xảy ra khi ở giữa một trong các phương pháp của khóa, trạng thái khóa có thể bị hỏng, gây ra các lỗi chết tiếp theo, các ngoại lệ chưa được giải quyết và (đáng buồn) do việc sử dụng các khóa spin bên trong, một CPU 100% được chốt. Vì vậy, nếu bạn định chạy mã trong môi trường thường xuyên sử dụng hủy bỏ chuỗi hoặc nỗ lực để tồn tại các OOM khó, bạn sẽ không hài lòng với khóa này.

+0

Nếu ai đó đang ném xung quanh ThreadAbortExceptions thì có những vấn đề nghiêm trọng hơn nhiều so với chỉ là bế tắc. Sau đó, thời gian CHỈ một ThreadAbortException là thích hợp là khi nó được nâng lên bởi chính nó như khi gọi HttpResponse.End. –

+0

Tôi nghĩ đây là một điểm tuyệt vời và sẽ nhận được nhiều sự chú ý hơn. Tôi thực sự bị cuốn hút bởi câu trả lời của Marc Gravell cho đến khi tôi đọc nó. – Pandincus

+1

... cả hai liên kết đều đã chết. – Beachwalker

Các vấn đề liên quan