简体   繁体   English

ReaderWriterLock用于数组

[英]ReaderWriterLock for array

I have an array, that represents an inventory, with nearly 50 elements (items: some costum objects) and I need a readerwritelock for it (okay, i think a simple lock would be enough too). 我有一个数组,代表一个库存,有近50个元素(项目:一些costum对象),我需要一个readerwritelock(好吧,我认为一个简单的锁也足够了)。 It should support both reference changing and value changing. 它应该支持参考变化和价值变化。

As reading and writing to different position of the array is threadsafe ( Proof ) I want to ensure that multiple read/write operations on the same array position is also threadsafe. 由于读取和写入数组的不同位置是线程安全的( Proof ),我想确保在同一阵列位置上的多个读/写操作也是线程安全的。

I surely could create 50 readerwriterlocks, but I don't want that ;) Is there a way to archive this? 我当然可以创建50个readerwriterlocks,但我不希望这样;)有没有办法存档这个? (I know ConcurrentList/Dictionary/etc. but I want an array...) (我知道ConcurrentList / Dictionary / etc。但我想要一个数组...)

Thanks 谢谢

If you are replacing the references in the array, then this is already safe, since reference swaps are inherently atomic. 如果要替换数组中的引用,那么这已经是安全的,因为引用交换本质上是原子的。 So you can use: 所以你可以使用:

var val = arr[index];
// now work with val

and

var newVal = ...
arr[index] = newVal;

perfectly safely, at least in terms of avoiding torn references. 完全安全,至少在避免撕裂引用方面。 So one pragmatic option is to make the object immutable , and just employ the above. 因此,一个实用的选择是使对象不可变 ,并使用上面的。 If you need to change the value, take a local copy, make a new version based from that, and then swap them. 如果需要更改值,请使用本地副本,根据该副本创建新版本,然后交换它们。 If lost updates are a problem, then Interlocked.CompareExchange and a re-apply loop can be used very successfully (ie you keep reapplying your change until you "win"). 如果丢失更新是一个问题,那么Interlocked.CompareExchange和重新应用循环可以非常成功地使用(即您继续重新应用您的更改,直到您“赢”)。 This avoids the need for any locking. 这避免了任何锁定的需要。

If, however, you are mutating the individual objects, then the game changes. 但是,如果您正在改变单个对象,则游戏会发生变化。 You could make the object internally thread-safe, but this is usually not pretty. 你可以让对象在内部是线程安全的,但这通常不是很好。 You could have a single lock for all the objects. 您可以为所有对象设置一个锁。 But if you want granular locking then you will need multiple locks. 但是如果你想要粒度锁定,那么你需要多个锁。

My advice: make the object immutable and just use the atomic reference-swap trick. 我的建议:使对象不可变,只使用原子引用交换技巧。

First off, you may not need any locks. 首先,您可能不需要任何锁。 Reading and writing with an array of a type where the CPU would handle each read and write atomically, is in and of itself thread-safe (but you might want to put in a memory barrier to avoid stale reads). 读取和写入一个类型的数组,其中CPU将以原子方式处理每个读取和写入,本身就是线程安全的(但是您可能希望放入内存屏障以避免过时读取)。

That said, just like x = 34 for an integer is threadsafe but x++ is not, if you've writes that depend upon the current value (and which are hence a read and a write), then that is not threadsafe. 也就是说,就像一个整数的x = 34是线程安全但x++不是,如果你的写入依赖于当前值(因此是读取和写入),那么这不是线程安全的。

If you do need locks, but don't want as many as 50, you could stripe. 如果你确实需要锁,但不想多达50,你可以条纹。 First set up your striped locks (I'll use simple locks rather than ReaderWriterSlim for smaller example code, the same principle applies): 首先设置你的条带锁(我将使用简单的锁而不是ReaderWriterSlim用于更小的示例代码,同样的原则适用):

var lockArray = new object[8];
for(var i =0; i != lockArray.Length; ++i)
  lockArray[i] = new object();

Then when you go to use it: 然后当你去使用它时:

lock(lockArray[idx % 8])
{
  //operate on item idx of your array here
}

It's a balance between the simplicity and size of one lock for everything, vs the memory use of one lock for each element. 它是一个锁的简单性和大小之间的平衡,与每个元素的一个锁的内存使用相比。

The big difficulty comes in if an operation on one element depends on that of another, if you need to resize the array, or any other case where you need to have more than one lock. 如果对一个元素的操作依赖于另一个元素的操作,如果需要调整数组大小,或者需要具有多个锁定的任何其他情况,则会出现最大的困难。 A lot of deadlock situations can be avoided by always acquiring the locks in the same order (so no other thread needing more than one lock will try to get one you already have while holding one you need), but you need to be very careful in these cases. 通过始终以相同的顺序获取锁定可以避免许多死锁情况(因此没有其他需要多个锁的线程会尝试获取您已经拥有的锁定而不需要),但是您需要非常小心这些案件。

You also want to make sure that if you are dealing with say, index 3 and index 11, you avoid locking on object 3 twice (I can't think of a way this particular recursive locking would go wrong, but why not just avoid it rather than have to prove it's one of the cases where recursive locking is safe?) 你还要确保如果你正在处理索引3和索引11,你可以避免两次锁定对象3(我想不出这种特殊的递归锁定会出错的方法,但为什么不避免它而不是必须证明它是递归锁定是安全的情况之一?)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM