ScopeLock模式:结构还是类?

时间:2018-09-14 15:23:12

标签: c# .net

我习惯使用“使用”语句来实现一些范围锁定模式。

示例:

ReaderWriterLockSlim _storageLocker
using (_storageLocker.LockRead())
{
   // do suff
}

在此示例中,扩展方法LockRead创建一个特定的IDisposable对象,该对象将在构造时锁定并在释放时释放。

/// <summary>
/// Scope lock pattern that lock the current <see cref="ReaderWriterLockSlim"/> in read mode
/// </summary>
public static IDisposable LockRead(this ReaderWriterLockSlim locker, TimeSpan timeout = default(TimeSpan))
{
    if (timeout == default(TimeSpan))
        timeout = s_defaultTimeout;

    var isLocked = locker.TryEnterReadLock(timeout);

    if (isLocked)
        return new ScopeLockAction<ReaderWriterLockSlim>(l => l.ExitReadLock(), locker);

    return Disposable.Disposed;
}

此模式比try/Finally有用且更干净,但可悲的是,每次锁定时都会创建一个新实例。

ScopeLockAction正确地实现了IDispose模式,并调用了GC.SuppressFinalizer()以稍微优化回收率。

我知道.NET garbadge收集器实现了某种回收机制,使他可以为将来的相同类型实例重用分配的空间。

我的问题是:

  • 当您有一个固定大小的小型实例,该实例通常会创建和处理,并且仅与using语句一起使用(这意味着没有装箱)时,使用classstruct

  • 有什么区别吗?

  • 是否可以通知Garbadge收集器它可以为另一个相同类型的实例回收实例空间?

2 个答案:

答案 0 :(得分:0)

来自帖子answer中的What are the uses of "using" in C#

  

“ using”语句的原因是确保该对象是   一旦超出范围就立即处置,不需要   明确的代码以确保发生这种情况。

结构是值类型,类是引用类型,我认为,使用“ using”语句并不重要,因为一旦超出范围,它就会被销毁。
您可以在Finalize/Dispose pattern in C#

中找到有关IDispose / Finalize事物使用的一些很好的讨论。

答案 1 :(得分:0)

    如果没有装箱,则
  1. 微小结构会更快。 (根据我的测试,它可以使结构类似的对象快7到20倍)。但是最好做个测试!
  2. 取决于计数对象,但是是的,结构将减少边成本
  3. 没有办法通知GC,仅会使用内部启发式方法

因此代码将如下所示,请注意,当TryEnterReadLock返回false时,您必须检查大小写:

        var _storageLocker = new ReaderWriterLockSlim();

        using (var lockContext =new ReadLockContext(_storageLocker))
        {
            if (lockContext.IsAcquired)
            {

            }
        }

public struct ReadLockContext : IDisposable
{
    private readonly ReaderWriterLockSlim locker;

    public ReadLockContext(ReaderWriterLockSlim locker, TimeSpan timeout = default(TimeSpan))
    {
        if (timeout == default(TimeSpan))
            timeout = TimeSpan.FromMilliseconds(-1);

        if (locker.TryEnterReadLock(timeout))
            this.locker = locker;
        else
            this.locker = null;
    }

    public bool IsAcquired => locker != null;

    public void Dispose()
    {
        locker?.ExitReadLock();
    }
}