首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >ReaderWriterLockSlim问题

ReaderWriterLockSlim问题
EN

Stack Overflow用户
提问于 2017-05-17 23:11:42
回答 2查看 1K关注 0票数 0

在他的回答中,https://stackoverflow.com/a/19664437/4919475

斯蒂芬·克利里

ReaderWriterLockSlim是线程仿射锁类型,因此通常不能与异步和等待一起使用

他所说的“通常”是什么意思?什么时候可以使用ReaderWriterLockSlim

另外,我在这里读过http://joeduffyblog.com/2007/02/07/introducing-the-new-readerwriterlockslim-in-orcas/ReaderWriterLockSlim有不同的怪癖,但本文是2007年的。从那以后就变了吗?

EN

回答 2

Stack Overflow用户

发布于 2017-05-18 00:14:57

我猜你发布了一个只有Cleary才能回答的问题,因为你想知道他的意思。

同时,从他的声明中可以明显地推断,在任何情况下,如果您能够保证获得锁的相同线程也能够释放锁,就可以在任何情况下将ReaderWriterLockSlimasync/await一起使用。

例如,您可以想象这样的代码:

代码语言:javascript
复制
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上下文的自然结果。无论哪种方式,这些场景都不像Click示例那样与Click兼容。

(我故意忽略了一个更大的问题,即获取一个锁,然后在可能长期运行的异步操作期间保持它是否是一个好主意。那就是,就像人们所说的,“一个完整的‘另一个’蜡球‘。”

增编:

对于我忽略…的“更大的问题”,“简短”的评论太长了,而不是一个实际的评论。

“更大的问题”是相当广泛和高度依赖上下文的。所以我才没说出来。简短的版本分为两部分:

  1. 一般来说,锁应该保持很短的时间,但是在一般情况下,异步操作的持续时间可能很长,因此这两个操作是相互不愉快的。锁在执行并发操作时是必要的,但是它们在某种程度上会否定并发操作的好处,因为它们具有序列化其他并发操作的效果。

持有锁的时间越长,一个或多个线程被阻塞等待的可能性就越大,它们会序列化任何工作。他们都在等待同一个锁,所以即使长时间运行的锁被释放,他们仍然必须按顺序工作,而不是同时工作。这有点像交通堵塞,一长队的汽车在等着一辆建筑卡车堵住道路,…即使卡车不挡道,也要花一些时间才能解决堵塞问题。

我不会说在异步操作期间保持锁本身就不好--也就是说,我可以想象出仔细考虑的情况--这样做是可以的--但它常常会破坏其他实现目标,而且在某些情况下,可以完全撤销本来要进行大量并发的设计,特别是在没有特别小心的情况下。

  1. 从语义上讲,很容易出错,例如,在await中,您知道锁仍然存在,但是“触发和遗忘”并不少见,并且会导致代码在异步操作发生时出现锁定,但实际上并非如此(请参阅Stack溢出问题在调用/BeginInvoke期间,锁会发生什么情况?(事件调度)中的一个例子,该问题就是这样做的,甚至没有意识到这一点)。避免错误代码的一种方法就是简单地避免已知的可能导致错误的编码模式。

同样,如果一个人足够小心,一个人可以避免错误。但是,通常更好的做法是简单地更改实现,以使用不太复杂的方法,并养成这样做的习惯。

票数 5
EN

Stack Overflow用户

发布于 2018-11-16 14:32:06

我在这个问题上注意到你问过:

你能解释一下“任意代码”是什么意思吗?

我相信,这份说明强调了“更大的问题”的一个重要方面,我将尝试-在时间紧迫的情况下-简短地谈到这个问题。这里的一个主要关注点是,等待语句不能保证它等待的Task将在与调用代码相同的上下文中运行(特别是在线程仿射锁的情况下,在同一个线程上);实际上,这将破坏Task承诺的许多目的。

假设您正在等待的Task正在等待使用Task.Run创建的Task,或者是在另一个线程上,或者已经生成当前线程等待某些后台资源(比如磁盘或网络I/O)。在这些情况下,至少有两种意想不到的行为很容易意外地被发现:

  1. 如果在另一个线程中执行的代码试图获得与等待它的调用代码相同的锁;调用线程拥有锁,并且由于子任务在另一个线程上执行,所以在调用线程释放锁之前它无法获得锁,这是因为它正在等待尚未完成的子任务。如果第二次试图锁定的线程与第一个线程相同,则锁将识别该线程已获得该锁,并允许进行第二次锁定尝试。由于它们不在同一个线程上,这将成为一个依赖于自身的死锁,并且将停止调用线程和子任务,或者将超时,这取决于所使用的锁定方法。大多数其他死锁需要跨多个代码路径以不同顺序使用2个或更多个锁,其中每个路径持有另一个正在等待的锁。
  2. 如果调用线程是UI线程(或具有消息泵的其他上下文,它可以在上一次请求等待异步行为时继续处理请求),则假设它等待在另一个线程中执行Task,该线程需要足够长的时间来处理消息泵开始处理另一条消息(例如再次单击同一按钮,或任何其他可能需要相同锁的“任意代码”),则该新消息将在拥有该锁的同一个线程上执行,因此即使上一次Task尚未完成,也允许继续执行该消息,从而允许任意访问应该同步的资源。

虽然前者可能导致您的应用程序或它的某些组件被锁定,但后者可能会产生非常意外的结果,并且特别难以解决疑难解答问题。所有线程仿射锁定机制都存在类似的情况(比如Monitor,它是lock关键字的底层实现)。希望这能有所帮助。

如果您对C#中的并行模式有更多的兴趣,我可能会推荐免费的C#中的线程化电子书(这实际上是摘自其他优秀书籍"C# in a which“)。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/44036160

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档