2

如果我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:这里的“写”锁用于指示写入数组引用,而不是数组元素。我假设这已经足够了(因为数据本身是不可变的)。我想我需要在递增时使用 Interlocked Count。真的吗?

4

2 回答 2

3

我完全希望写锁能够充当内存屏障(特别是在写锁内),但我无法立即证明这一点。

是否需要复杂度ReaderWriterLockSlim取决于上下文;Interlocked, volatile,lock或者[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();
        }
    }

更新了您的编辑;

这看起来很扎实。AList<T>将是我的建议(通过T[]数组),因为它会在内部完成所有加倍等操作,从而为您节省大量代码。由于一次只能更新一个线程Count,因此不需要Interlocked,并且此属性在读取 时节省了对锁的需要Count,只要您可以让调用者Count在另一个线程添加行时获取旧的(而不是被封锁)。

于 2008-12-20T15:34:56.393 回答
1

的,对于各种内存屏障案例的非常深入的概述,请查看该文档,如果您还需要,您可以找到非围栏锁。

并且请不要使用 VOLITILE,这些天它的效果越来越差!!

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

内存屏障是从任何其他处理器的角度保留读取和/或写入操作顺序的处理器指令。内存屏障包括具有获取、释放和栅栏语义的处理器指令。这些语义描述了操作结果变得可见的顺序。

  • 获取语​​义意味着操作的结果在代码中出现在它之后的任何操作的结果之前是可见的。
  • 释放语义意味着操作的结果在代码中出现在它之前的任何操作的结果之后是可见的。
  • 栅栏语义结合了获取和释放语义。具有栅栏语义的操作的结果在代码中出现在它之后的任何操作的结果之前以及出现在它之前的任何操作的结果之后都是可见的。
于 2009-05-29T11:02:35.757 回答