与 ReaderWriterLock(或 ReaderWriterLockSlim)一起使用时,有序锁定模式是否可以防止死锁?
显然,该模式可以防止互斥锁死锁。如果我用((N 个带读锁的资源)和(1 个或 2 个带写锁的资源))锁定多个资源,它是否仍然可以防止死锁。
例如:(粗体数字代表具有写锁的资源)
1 2 3 4 5
2 3 4
1 4 5
与 ReaderWriterLock(或 ReaderWriterLockSlim)一起使用时,有序锁定模式是否可以防止死锁?
显然,该模式可以防止互斥锁死锁。如果我用((N 个带读锁的资源)和(1 个或 2 个带写锁的资源))锁定多个资源,它是否仍然可以防止死锁。
例如:(粗体数字代表具有写锁的资源)
1 2 3 4 5
2 3 4
1 4 5
tl; dr 资源排序也可以防止读写锁死锁。
长答案:
画一个这样的等待图:
首先按照规定的顺序从左到右为所有资源绘制一个顶点。
然后,对于每个等待资源的进程,在等待资源的顶点之前绘制一个顶点。如果进程没有等待任何资源,则在所有资源顶点之后在最右侧绘制一个顶点。
然后从每个进程到该进程正在等待的资源绘制一条边,并从每个资源到当前持有该资源的进程绘制一条边。
考虑图上的每条边。有两种情况:
边缘是Pi -> Ri
,即从进程到资源。由于每个进程只等待一个资源,并且我们将进程的顶点绘制到它正在等待的资源的左侧,所以边是从左到右的。
边缘是Ri -> Pj
,即从资源到进程。如果Pj
没有等待任何资源,那么它的顶点在所有资源的右边,因此边是从左到右的。如果Pj
正在等待Rk
,则i < k
,因为进程按顺序获取资源。如果i < k
,则Ri
在 的左侧Rk
,并且Pj
立即在 的左侧Rk
(因为我们以这种方式绘制了图形),因此Ri
在 的左侧Pj
,因此边缘再次从左到右。
由于这样绘制的图的所有边都是从左到右的,那么我们构造了图的拓扑排序,因此图没有环,因此不会发生死锁。
请注意,重要的是进程等待的事实,而不是等待的原因。因此,无论它是否等待互斥锁、读写锁、信号量或其他任何东西——这种防止死锁的策略都适用。
我认为你做错了有序锁定模式,你必须按顺序抓住所有的锁。要抢“4”的写锁,首先需要抢“3”的写锁。对于“5”,您需要获取“3” 、 “4”和“5”的写锁。
使用普通锁,有序锁定模式很昂贵,但使用 SRW 锁,它们非常昂贵,因为您必须先抛出所有读取器。请记住,只有在至少 80% 以上的访问不需要写入锁时,SRW 锁才有优势 - 否则它会非常昂贵,而且您可以使用简单的锁做得更好。
更好的是,尝试将您的代码解耦,这样您就不需要有序锁定(即,我不需要同时访问来自“3”和“4”的数据)。这并不总是可能的,但你的代码越简单,你越能做到这一点。
我尝试在 C# 中实现有序锁,以检查是否已按固定顺序获取锁。请参阅http://www.codeproject.com/Tips/563154/OrderedLock-in-Csharp