带Action的ReaderWriterLockSlim扩展

时间:2011-09-12 20:31:33

标签: c# multithreading lambda extension-methods

考虑以下扩展:

public static class ReaderWriteExt
{
    public static void ExecWriteAction(this ReaderWriterLockSlim rwlock, Action action)
    {
        rwlock.EnterWriteLock();
        try
        {
            action();
        }
        finally
        {
            rwlock.ExitWriteLock();
        }
    }
    public static void ExecUpgradeableReadAction(this ReaderWriterLockSlim rwlock, Action action)
    {
        rwlock.EnterUpgradeableReadLock();
        try
        {
            action();
        }
        finally
        {
            rwlock.ExitUpgradeableReadLock();
        }
    }
}

还要考虑以下示例用法(删除一些支持代码):

private static ReaderWriterLockSlim _rwlock = new ReaderWriterLockSlim();
private static ... _cacheEntries = ....;

public static void RemoveEntry(string name)
{
    WeakReference outValue = null;
    _rwlock.ExecUpgradeableReadAction(() =>
        {                    
            if (_cacheEntries.TryGetValue(name, out outValue))
            {
                if (!outValue.IsAlive)
                {
                    _rwlock.ExecWriteAction(() => _cacheEntries.Remove(name));
                }
            }
        });
}

我是C#编码的新手,我无法找到有关这些主题的足够信息可以指导我。对于我的问题:我正在考虑在我们的生产代码中使用这个概念,这是一个坏主意吗?什么可能出错?

3 个答案:

答案 0 :(得分:2)

除了代码看起来非常麻烦外,这对我来说似乎很好

我可能会将IDisposable实现为:

public class WriteLock : IDisposable
{
   ReaderWriterLockSlim _rwlock;
   public WriteLock(ReaderWriterLockSlim rwlock ) 
   { 
      _rwlock = rwlock;
      _rwlock.EnterWriteLock(); 
   }
   public void Dispose()
   {
      _rwlock.ExitWriteLock(); 
   }
}

用法:

 private ReaderWriterLockSlim _rwlock = new ReaderWriterLockSlim();

 //...

 using (new WriteLock(_rwlock)) //<-- here the constructor calls EnterWriteLock
 {
      _cacheEntries.Remove(name);

 } //<---here Dispose method gets called automatically which calls ExitWriteLock

同样,您可以实现UpgradeableReadLock类实现IDisposable接口。

这个想法是你可以在using构造中创建一个一次性类的实例,它确保在构造函数中通过调用EnterWriteLock()方法进入写锁定,当它超出范围时,自动调用Dispose()方法(通过CLR)调用ExitWriteLock()方法。

请注意,它不会处置ReaderWriterLockSlim个对象;它将处理WriteLock对象,它只是一个包装器。 ReaderWriterLockSlim将在用户类中使用。

答案 1 :(得分:2)

我没有看到任何理由不安全的原因。但是,我怀疑它实际上比使用普通的lock慢。原因是因为ReaderWriterLockSlim的开销大约是lock的2倍。 1 所以你需要在关键部分执行代码才能消耗足够的足够数量的CPU周期来克服这个增加的开销只是为了达到盈亏平衡点。如果你所做的只是访问Dictionary(或恰好是_cacheEntries的任何数据结构),那么我怀疑RWLS是否适合这种情况。读取器 - 写入器锁在代码保护部分很长并且被抽出时读取器的数量明显超过编写器的情况下往往更好地工作。显然你应该做自己的基准测试,因为里程会因很多其他因素而有很大差异。硬件中的并行度和内核数量可以使用RWLS为您提供更多吞吐量,即使简单的盈亏平衡点分析最初并不支持它们。


1 基于我自己的测试。 ReaderWriterLock的开销约为5倍。

答案 2 :(得分:2)

是的,有技术上的原因,为什么这不是一个好主意。如果Action在更新数据时抛出异常,则数据现在处于未知且非常可能已损坏的状态。您的代码无条件释放编写器锁,这意味着其他线程现在正在访问部分更新的共享状态。这是灾难的秘诀。