在他的回答中,https://stackoverflow.com/a/19664437/4919475
斯蒂芬·克雷里(Stephen Cleary)提到
ReaderWriterLockSlim是线程仿射锁定类型,因此通常
不能与异步和等待一起使用。
他“通常”是什么意思?什么时候可以使用ReaderWriterLockSlim
?
另外,我在这里http://joeduffyblog.com/2007/02/07/introducing-the-new-readerwriterlockslim-in-orcas/读到ReaderWriterLockSlim
具有不同的怪癖,但是本文来自2007年。此后是否有所改变?
最佳答案
我想您已经发布了一个只有Cleary可以回答的问题,因为您想知道他的意思。
同时,从他的陈述中得出的明显推论是,在能够保证获得锁的同一线程也能够执行的任何情况下,您都可以将ReaderWriterLockSlim
与async
/ await
一起使用释放它。
例如,您可以想象这样的代码:
private readonly ReaderWriterLockSlim _rwls = new ReaderWriterLockSlim();
async void button1_Click(object sender, EventArgs e)
{
_rwls.EnterWriteLock();
await ...;
_rwls.ExitWriteLock();
}
在上面的代码中,由于
Click
事件将在await
返回的线程中引发,因此您可以获取锁,执行await
,并且仍然可以继续释放锁,因为您知道它将是同一线程。在
async
/ await
的许多其他用法中,不能保证延续在产生该方法的线程中进行,因此不允许释放在await
之前获得它的锁。 。在某些情况下,这显然是故意的(即ConfigureAwait(false)
),在其他情况下,这只是await
上下文的自然结果。无论哪种方式,这些方案都不符合ReaderWriterLockSlim
示例的方式与Click
兼容。(我故意忽略了一个更大的问题,即获取一个锁然后在可能长时间运行的异步操作期间握住它是否是一个好主意。也就是说,正如他们所说,“整个'另一个球或蜡' ”。
附录:
关于我忽略的“较大问题”的“简短”评论,太长了,无法成为实际评论。
“更大的问题”是相当广泛的,并且高度依赖于上下文。这就是为什么我没有解决。简短版本分为两部分:
通常,锁应保持很短的时间,但是一般而言,异步操作的持续时间可能很长,因此两者是互不相容的。当执行并发操作时,锁是必不可少的罪恶,但在某种程度上它们总是会抵消并发操作的好处,因为它们具有序列化否则并发操作的效果。您持有锁的时间越长,锁的可能性就越大。或更多线程被阻塞以等待某事,序列化它们所进行的任何工作。它们都在等待同一个锁,因此即使释放了长期运行的锁,它们也都将必须按顺序工作,而不是同时进行。这有点像交通拥堵,一排排的汽车正在等待工程卡车完成对路的阻塞……即使卡车不碍事,清除它也会花费一些时间。我不会说在异步操作期间保持锁本来就是不好的-也就是说,我可以想像仔细考虑的方案,在这种情况下是可以的-但它通常会破坏其他实现目标,并且在某些情况下可以完全撤消旨在大量并发,尤其是在没有非常小心的情况下。
从语义上讲,很容易犯一个错误,即使用
await
您知道锁定会持续一段时间,但是“发射后忘记”并不少见,并且会导致代码在异步操作发生时似乎已锁定,但实际上并非如此(请参阅“堆栈溢出”问题What happens to a lock during an Invoke/BeginInvoke? (event dispatching),该示例确实做了某件事,甚至没有意识到这一点)。避免错误代码的一种方法是简单地避免已知可能导致错误的编码模式。再次,如果足够谨慎,就可以避免错误。但是通常最好只是简单地更改实现,以使用不太麻烦的方法,并养成这样做的习惯。
关于c# - ReaderWriterLockSlim问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44036160/