Desde el punto de una cerradura es proteger un poco de memoria, creo que sería útil envolver esa memoria en un objeto "Bloqueado", y solo hacerla accesible a través de varios tokens de bloqueo (como se menciona en 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);
}
Ahora la única manera de acceder a la lista interna es llamando el descriptor de acceso apropiado.
Esto funciona especialmente bien para List<T>
, ya que ya tiene la envoltura ReadOnlyCollection<T>
. Para otros tipos, siempre puede crear un Locked<T,T>
, pero luego pierde la buena distinción de tipo legible/grabable.
Una mejora podría ser la definición de los R
y W
tipos como ellos mismos envoltorios desechables, lo cual protegidos contra errores (inadvertidos) como:
List<T> list;
using(var w = lockedList.Write())
list = w.Writable;
//BAD: "locked" object leaked outside of lock scope
list.MakeChangesWithoutHoldingLock();
Sin embargo, esto haría que Locked
más complicado de usar, y la la versión actual sí le brinda la misma protección que tiene cuando bloquea manualmente un miembro compartido.
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
}
+1 gran fragmento! –
Es curioso, hace tiempo que me había olvidado de esto y, por pura casualidad, aparece en mi reputación de la misma manera que podría beneficiarme de ello. –