这对我来说似乎很吵。五行开销实在是太多了。
m_Lock.EnterReadLock()
Try
Return m_List.Count
Finally
m_Lock.ExitReadLock()
End Try
那你怎么会这么简单?
这对我来说似乎很吵。五行开销实在是太多了。
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();
}
这意味着在 sync.Read() 和 try 块之间可能会发生 ThreadAbortException(或类似情况)。当这种情况发生时,finally 块永远不会被调用,并且锁永远不会被释放!
有关更多信息和更好的实现,请参阅:
Deadlock with ReaderWriterLockSlim and other lock objects。简而言之,更好的实现归结为将锁移动到try
块中,如下所示:
ReaderWriterLockSlim myLock = new ReaderWriterLockSlim();
try
{
myLock.EnterReadLock();
// Do stuff
}
finally
{
// Release the lock
myLock.ExitReadLock();
}
像接受的答案中的包装类将是:
/// <summary>
/// Manager for a lock object that acquires and releases the lock in a manner
/// that avoids the common problem of deadlock within the using block
/// initialisation.
/// </summary>
/// <remarks>
/// This manager object is, by design, not itself thread-safe.
/// </remarks>
public sealed class ReaderWriterLockMgr : IDisposable
{
/// <summary>
/// Local reference to the lock object managed
/// </summary>
private ReaderWriterLockSlim _readerWriterLock = null;
private enum LockTypes { None, Read, Write, Upgradeable }
/// <summary>
/// The type of lock acquired by this manager
/// </summary>
private LockTypes _enteredLockType = LockTypes.None;
/// <summary>
/// Manager object construction that does not acquire any lock
/// </summary>
/// <param name="ReaderWriterLock">The lock object to manage</param>
public ReaderWriterLockMgr(ReaderWriterLockSlim ReaderWriterLock)
{
if (ReaderWriterLock == null)
throw new ArgumentNullException("ReaderWriterLock");
_readerWriterLock = ReaderWriterLock;
}
/// <summary>
/// Call EnterReadLock on the managed lock
/// </summary>
public void EnterReadLock()
{
if (_readerWriterLock == null)
throw new ObjectDisposedException(GetType().FullName);
if (_enteredLockType != LockTypes.None)
throw new InvalidOperationException("Create a new ReaderWriterLockMgr for each state you wish to enter");
// Allow exceptions by the Enter* call to propogate
// and prevent updating of _enteredLockType
_readerWriterLock.EnterReadLock();
_enteredLockType = LockTypes.Read;
}
/// <summary>
/// Call EnterWriteLock on the managed lock
/// </summary>
public void EnterWriteLock()
{
if (_readerWriterLock == null)
throw new ObjectDisposedException(GetType().FullName);
if (_enteredLockType != LockTypes.None)
throw new InvalidOperationException("Create a new ReaderWriterLockMgr for each state you wish to enter");
// Allow exceptions by the Enter* call to propogate
// and prevent updating of _enteredLockType
_readerWriterLock.EnterWriteLock();
_enteredLockType = LockTypes.Write;
}
/// <summary>
/// Call EnterUpgradeableReadLock on the managed lock
/// </summary>
public void EnterUpgradeableReadLock()
{
if (_readerWriterLock == null)
throw new ObjectDisposedException(GetType().FullName);
if (_enteredLockType != LockTypes.None)
throw new InvalidOperationException("Create a new ReaderWriterLockMgr for each state you wish to enter");
// Allow exceptions by the Enter* call to propogate
// and prevent updating of _enteredLockType
_readerWriterLock.EnterUpgradeableReadLock();
_enteredLockType = LockTypes.Upgradeable;
}
/// <summary>
/// Exit the lock, allowing re-entry later on whilst this manager is in scope
/// </summary>
/// <returns>Whether the lock was previously held</returns>
public bool ExitLock()
{
switch (_enteredLockType)
{
case LockTypes.Read:
_readerWriterLock.ExitReadLock();
_enteredLockType = LockTypes.None;
return true;
case LockTypes.Write:
_readerWriterLock.ExitWriteLock();
_enteredLockType = LockTypes.None;
return true;
case LockTypes.Upgradeable:
_readerWriterLock.ExitUpgradeableReadLock();
_enteredLockType = LockTypes.None;
return true;
}
return false;
}
/// <summary>
/// Dispose of the lock manager, releasing any lock held
/// </summary>
public void Dispose()
{
if (_readerWriterLock != null)
{
ExitLock();
// Tidy up managed resources
// Release reference to the lock so that it gets garbage collected
// when there are no more references to it
_readerWriterLock = null;
// Call GC.SupressFinalize to take this object off the finalization
// queue and prevent finalization code for this object from
// executing a second time.
GC.SuppressFinalize(this);
}
}
protected ~ReaderWriterLockMgr()
{
if (_readerWriterLock != null)
ExitLock();
// Leave references to managed resources so that the garbage collector can follow them
}
}
用法如下:
ReaderWriterLockSlim myLock = new ReaderWriterLockSlim();
using (ReaderWriterLockMgr lockMgr = new ReaderWriterLockMgr(myLock))
{
lockMgr.EnterReadLock();
// Do stuff
}
此外,来自Joe Duffy 的博客
其次,锁对异步异常(如线程中止和内存不足)不可靠。如果其中一种发生在锁定方法之一的中间,则锁定状态可能会损坏,从而导致后续死锁、未处理的异常,并且(可悲的是)由于内部使用自旋锁而导致 100% 的 CPU 挂钩。因此,如果您要在经常使用线程中止或尝试在硬 OOM 中生存的环境中运行代码,您将不会对这种锁感到满意。
这不是我的发明,但它确实使头发少了一点灰白。
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
由于锁的目的是保护某些内存,我认为将内存包装在“锁定”对象中会很有用,并且只能通过各种锁令牌访问它(如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
andW
类型定义为一次性包装器本身,这将防止(无意的)错误,例如:
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
}