使用ReaderWriterLockSlim会导致内存障碍吗?

时间:2008-12-20 15:32:34

标签: .net multithreading locking

如果我使用ReaderWriterLockSlim获取读/写锁,我是否需要创建变量volatile或使用Interlocked.Increment

例如,下面Add方法中的代码是否正常工作,还是需要增强?

public class AppendableList<T> { // semi-immutable; supports appending only
    private T[] data = new T[16];
    private ReaderWriterLockSlim rwLock = new ReaderWriterLockSlim();
    public int Count { get; private set; }
    public T this[int index] {
        get {
            rwLock.EnterReadLock();
            try { return data[index]; } finally { rwLock.ExitReadLock(); }
        }
    }
    public void Add(T item) {
        rwLock.EnterUpgradeableReadLock();
        try {
            if (Count == data.Length)
                reAllocateArray(); // upgrades to write lock
            data[Count++] = item; // do I need to use Interlocked here?
        } finally { rwLock.ExitUpgradeableReadLock(); }
    }
}

编辑:我正在尝试编写一个轻量级,快速且简单的列表,允许多个线程同时访问其数据(生成器 - 消费者缓冲区)。我已经编辑了上面的代码,删除了我之前使用的简化,所以问题现在应该更清楚了。在我看来,这段代码是线程安全的,但我不确定所有线程是否会在退出可升级锁后立即看到Count的更新值。

编辑2 :此处的“写入”锁用于表示写入数组引用,而不是数组元素。我假设这已经足够了(因为数据本身是不可变的)。我猜我需要在递增Count时使用Interlocked。这是真的吗?

2 个答案:

答案 0 :(得分:3)

我完全期望写锁定作为内存屏障(特别是在写锁定中),但我无法证明它是不可能的。

是否需要ReaderWriterLockSlim的复杂性取决于上下文; Interlockedvolatilelock[MethodImpl]可能会更简单地完成这项工作。如果你有很多读者和很少的作家,你主要需要ReaderWriterLock[Slim]

但是,get目前不受锁定保护;你需要我们一个显式的属性实现,如果你需要通过写锁来跨越多个操作(没有读者看到中间值),你自己取出一个读锁。

另外,人们可能更熟悉Count++用法。

您还应该使用try / finally来确保解除对异常的锁定。

为了避免写入然后读取锁定的问题,可能:

    private ReaderWriterLockSlim rwLock = new ReaderWriterLockSlim();

    private int count;
    public int Count {
        get {
            rwLock.EnterReadLock();
            int tmp = count;
            rwLock.ExitReadLock();
            return tmp;
        }
    }
    public void Add(object x) {
        rwLock.EnterWriteLock();
        try {
            // do some processing
            count++;
        } finally {
            rwLock.ExitWriteLock();
        }
    }

更新了您的编辑内容;

看起来很稳固。 List<T>将是我的推荐(超过T[]数组),因为它将在内部完成所有加倍等操作,从而为您节省大量代码。由于一次只有一个帖子可以更新Count,因此不需要Interlocked,只要你还好,这个属性就可以在阅读Count时节省锁定的需要调用者获取旧Count而另一个线程正在添加行(而不是被阻止)。

答案 1 :(得分:1)

Yes对于各种内存屏障情况的深入概述,检查该文档,如果需要,还可以找到非fence'd锁。

并请,请勿使用它们,这些日子不会太有效!

  

所有标准Windows锁定   机制(自旋锁,互斥锁,   内核事件和管理的资源   由执行资源包)   防止处理器重新排序   通过插入内存屏障   可执行代码中需要。

     

内存屏障是处理器   保留的指令   读和/或写的排序   从任何角度看操作   其他处理器。记忆障碍   包含处理器指令   获取,释放和围栅语义。   这些语义描述了顺序   哪个操作的结果变成了   可见。

     
      
  • 获取语义意味着操作的结果是可见的   在任何操作的结果之前   它出现在代码之后。
  •   
  • 释放语义意味着操作的结果是可见的   任何操作结果后   在代码中出现在它之前。
  •   
  • Fence语义结合了获取和释放语义。结果一个   使用fence语义的操作是   在任何操作之前都可见   它出现在代码之后   在任何操作之后   出现在它面前。
  •