5
|
jpfollenius Rob Kennedy · 技术社区 · 15 年前 |
![]() |
1
6
在这个问题中,似乎有两个标准:
因为其他人已经这样做了,所以我不会再进一步讨论第一点。然而,第二点非常微妙,需要解释。 首先,让我说我指的是Delphi2007。我不能进入2009年。然而,我描述的行为不太可能改变。 你显示的代码 做 使其他编写器可以在代码块期间更改值。当读锁升级为写锁时,读锁暂时丢失。当线程既没有读锁也没有写锁时,会有一个瞬间。这是设计的,否则死锁几乎是肯定的。 如果 将读锁升级为写锁的线程在执行此操作时实际上持有读锁,很容易发生以下情况:
为了防止这种情况发生,tmuliReadExclusiveWriteSynchronizer在获取写锁之前释放了一些“即时”的读锁。 (旁注:文章 Working with TMultiReadExclusiveWriteSynchronizer 在EDN上,在“锁定克里斯,我要……”一节中,似乎错误地暗示了我刚才提到的场景实际上会死锁。这可能是关于先前版本的Delphi的文章,也可能只是弄错了。或者我可能误解了它的说法。不过,看看文章中的一些评论。) 因此,不需要再假设任何关于上下文的内容,您所显示的代码几乎肯定是不正确的。在有读锁的情况下检查一个值,然后将其升级为写锁,并假定该值没有更改,这是一个错误。这是TMuliReadExclusiveWriteSynchronizer非常微妙的捕获。 下面是Delphi库代码中注释的几个选定部分:
这里有一些代码要尝试。创建名为Lock的全局tmultireadExclusiveWriteSynchronizer。创造两个全球性的繁荣:坏的和全球性的。然后启动每个线程的一个实例,并从主程序线程监视bad的值。
尽管它是非确定性的,但您可能很快(不到1秒)就会看到bad值被设置为true。所以基本上,您看到globalb的值是真的,然后当您再次检查它时,它是假的,即使这两个检查都发生在beginread/endread对之间(原因是因为其中也有beginwrite/endwrite对)。 我的个人建议:只是 从未 将读锁升级为写锁。这太容易出错了。在任何情况下,您都不会真正将读锁升级为写锁(因为您暂时失去了读锁),所以您也可以在代码中通过在beginwrite之前调用endread将其明确化。是的,这意味着你必须再次检查贝金华内部的情况。因此,对于您最初显示的代码,我甚至不需要使用读锁。从beginwrite开始,因为它 可以 决定写作。 |
![]() |
2
4
首先:您的endwrite代码驻留在tsimplerwsync中,它是IReadWriteSync的轻量级实现,而tmultireadExclusiveWriteSynchronizer则更加复杂。 第二:如果仍然有一些对EnterCriticalSection(flock)的开放调用(如beginread中的调用),那么在endwrite中对leaveCriticalSection(flock)的调用不会释放锁。 这意味着您的代码示例非常有效,无论您使用的是tsimplerwsync实例还是tmultireadexclusivewritesynchronizer实例,都应按预期工作。 |
![]() |
3
3
我没有Delphi2009,但我希望tmultireadExclusiveWriteSynchronizer的工作方式没有变化。我认为,在您的场景中使用“beginwrite”是一个返回布尔值的函数,这是正确的结构。确保在执行写操作之前检查其结果。 此外,在Delphi2006中,tmultireadExclusiveWriteSynchronizer类中有许多开发人员注释,还有一些调试代码。在使用之前,请确保您查看了实现。 |
![]() |
4
0
感谢Uwe Raabe和Tihauan的回答: tmultireadExclusiveWriteSynchronizer与此类嵌套锁定结构一起工作正常。endwrite并不能真正缓解读锁,因此很容易在一定时间内将读锁升级为写锁,然后在不受其他编写器干扰的情况下返回到读锁。 |