我有一个数组,它代表一个清单,有将近50个元素(项目:一些客户对象),我需要一个readerwritelock (好的,我想一个简单的锁也足够了)。它应该同时支持引用更改和值更改。
由于读写数组的不同位置是线程安全的(Proof),我希望确保同一数组位置上的多个读/写操作也是线程安全的。
我当然可以创建50个读写锁,但我不想这样;)有没有办法把它归档?(我知道ConcurrentList/Dictionary/等,但我想要一个数组...)
谢谢
发布于 2012-08-14 18:57:41
如果您要替换数组中的引用,那么这已经是安全的,因为引用交换本质上是原子的。因此,您可以使用:
var val = arr[index];
// now work with val和
var newVal = ...
arr[index] = newVal;完全安全,至少在避免被撕裂的引用方面是这样。因此,一个实用的选择是使用使对象成为不可变的,并使用上面的方法。如果您需要更改该值,请获取一个本地副本,基于该副本创建一个新版本,然后交换它们。如果丢失更新是一个问题,那么可以非常成功地使用Interlocked.CompareExchange和re-apply循环(即不断重新应用您的更改,直到您“赢”为止)。这就避免了任何锁定的需要。
但是,如果你改变了单个对象,那么游戏就会改变。你可以让对象在内部是线程安全的,但这通常并不美观。你可以为所有的对象设置一个锁。但是如果你想要粒度锁,那么你将需要多个锁。
我的建议是:让对象不可变,只使用原子引用交换技巧。
发布于 2012-08-14 18:57:15
首先,您可能不需要任何锁。使用一种类型的数组进行读写操作本身就是线程安全的(但您可能希望设置内存屏障以避免陈旧的读操作)。
也就是说,就像整数的x = 34是线程安全的,但x++不是,如果你的写依赖于当前值(因此是读和写),那么那就不是线程安全。
如果您确实需要锁,但又不想要多达50个锁,那么您可以使用条纹。首先设置您的条纹锁(对于较小的示例代码,我将使用简单的锁而不是ReaderWriterSlim,原理相同):
var lockArray = new object[8];
for(var i =0; i != lockArray.Length; ++i)
lockArray[i] = new object();然后当你要使用它的时候:
lock(lockArray[idx % 8])
{
//operate on item idx of your array here
}它平衡了一个锁的简单性和大小,以及每个元素使用一个锁的内存使用情况。
如果一个元素上的操作依赖于另一个元素的操作,如果你需要调整数组的大小,或者任何其他需要有多个锁的情况,那么最大的困难就来了。通过始终以相同的顺序获取锁,可以避免许多死锁情况(因此,需要多个锁的其他线程不会在持有所需锁的同时尝试获取已有的锁),但在这种情况下需要非常小心。
您还希望确保在处理索引3和索引11时,避免两次锁定对象3(我想不出这种特定的递归锁定会出错的方式,但为什么不直接避免它,而是必须证明递归锁定是安全的情况之一呢?)
https://stackoverflow.com/questions/11950628
复制相似问题