gpt4 book ai didi

c# - ReaderWriterLockSlim.EnterUpgradeableReadLock() 总是死锁?

转载 作者:太空狗 更新时间:2023-10-29 20:44:49 25 4
gpt4 key购买 nike

我很熟悉ReaderWriterLockSlim但尝试实现 EnterUpgradeableReadLock()最近在一个类(class)......不久之后我意识到当 2 个或更多线程运行代码时,这几乎肯定是一个有保证的死锁:

Thread A --> enter upgradeable read lock
Thread B --> enter upgradeable read lock
Thread A --> tries to enter write lock, blocks for B to leave read
Thread B --> tries to enter write lock, blocks for A to leave read
Thread A --> waiting for B to exit read lock
Thread B --> waiting for A to exit read lock

我在这里缺少什么?

编辑

添加了我的场景的代码示例。 Run()方法将被 2 个或更多线程同时调用。
public class Deadlocker
{
private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);

public void Run()
{
_lock.EnterUpgradeableReadLock();
try
{
_lock.EnterWriteLock();
try
{
// Do something
}
finally
{
_lock.ExitWriteLock();
}
}
finally
{
_lock.ExitUpgradeableReadLock();
}
}
}

最佳答案

在 OP 之后很长一段时间,但我不同意目前接受的答案。

声明Thread B --> enter upgradeable read lock是不正确的。来自 the docs

Only one thread can be in upgradeable mode at any time



并回应您的评论:它旨在用于与读写模式截然不同的用途。

TL;博士 .可升级模式很有用:
  • 如果写入者必须在写入之前检查共享资源,并且(可选)需要避免与其他写入者的竞争条件;
  • 并且在 100% 确定必须写入共享资源之前,它不应停止读取器;
  • 并且作者很可能会在执行检查后决定不应该写入共享资源。

  • 或者,在伪代码中,这里:
    // no other writers or upgradeables allowed in here => no race conditions
    EnterUpgradeableLock();
    if (isWriteRequired()) { EnterWriteLock(); DoWrite(); ExitWriteLock(); }
    ExitUpgradeableLock();

    提供“更好的性能” ÷ 比这个:
    EnterWriteLock(); if (isWriteRequired()) { DoWrite(); } ExitWriteLock();

    如果排他锁部分由于使用 SpinLock 而需要很长时间,则应谨慎使用。 .

    类似的锁结构

    Upgradeable 锁与 SQL 服务器非常相似 SIX lock (与 Intent to go eXclusive 共享)†。
  • 用这些术语重写上面的语句,可升级锁说“一个写者打算写一个资源,但想与其他读者共享它,同时它[双重]检查一个条件以查看它是否应该独占锁定并执行写“‡。

  • 如果不存在 Intent 锁,您必须在 eXclusive 锁内执行“我是否应该进行此更改”检查,这会损害并发性。

    为什么不能分享Upgradeable?

    如果可升级锁可与其他可升级锁共享,则可能与其他可升级锁所有者发生竞争条件。因此,您将需要在写入锁内再次检查一次,从而消除了在不阻止其他读取的情况下进行检查的好处。

    例子

    如果我们把所有的锁等待/进入/退出事件看作是顺序的,并且锁内部的工作是并行的,那么我们可以用“大理石”的形式写一个场景( e enter; w wait; x exit; cr 检查资源; mr 变异资源; R 共享/读取; U 意图/可升级; W 独占/写入):
    1--eU--cr--wW----eW--mr--xWxU--------------
    2------eR----xR----------------eR--xR------
    3--------eR----xR--------------------------
    4----wU----------------------eU--cr--xU----

    换句话说:T1 进入可升级/意图锁。 T4 等待可升级/意图锁。 T2 和 T3 进入读锁。 T1 同时检查资源,赢得比赛并等待排他/写锁。 T2&T3 退出他们的锁。 T1 进入排他/写锁并进行更改。 T4 进入 Upgradeable/Intent 锁,不需要更改并退出,不会阻塞 T2 同时进行另一次读取。

    在 8 个要点中...

    可升级锁是:
  • 被任何作家使用;
  • 谁可能先检查,然后出于任何原因决定不执行写入(失去竞争条件,或在 Getsert 模式中);
  • 谁不应该阻止 Readers,直到它知道它必须执行 Write;
  • 因此它将取出一个排他锁并这样做。

  • 如果以下情况之一适用(包括但不限于),则不需要升级:
  • 读者和作者之间的争用率writelock-check-nowrite-exit大约为零(写入条件检查非常快)-即 Upgradeable 构造不会帮助 Reader 吞吐量;
  • 有一个写者在写锁中写一次的概率是~1,因为:
  • ReadLock-Check-WriteLock-DoubleCheck如此之快,每万亿次写入只会导致一次竞赛失败;
  • 所有的变化都是独一无二的(所有的变化都必须发生,种族不可能存在);或
  • “最后一次更改获胜”(所有更改仍然必须发生,即使它们不是唯一的)

  • 如果 lock(...){...} 也不需要更合适,即:
  • 重叠读取和/或写入窗口的可能性很低(锁可以防止非常罕见的事件和保护极有可能的事件一样多,更不用说简单的内存屏障要求)
  • 您所有的锁获取都是可升级或写入的,从不读取 ('duh')

  • ÷ “性能”由您来定义

    † 如果将锁对象视为表,将 protected 资源视为层次结构中较低的资源,则此类比大致成立

    • 读取锁中的初始检查是可选的,可升级锁中的检查是强制性的,因此它可以用于单次或双重检查模式。

    关于c# - ReaderWriterLockSlim.EnterUpgradeableReadLock() 总是死锁?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21411018/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com