【问题标题】:After EnterWriteLock the lock is not held在 EnterWriteLock 之后,锁没有被持有
【发布时间】:2012-09-12 09:08:49
【问题描述】:

我有一个 AVL 树数据结构,其中每个节点都有自己的锁。这是因为有更多的写入者试图访问一个节点。

class Node
{
    public ReaderWriterLockSlim ww;
    // ...
    public Node()
    {
        ww = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
        // ...
    }
}
class AVL_tree
{
    public Node root;
    // ...
    public void Write(int value)
    {
        root = new Node();
        root.ww.EnterWriteLock();
        if (!root.ww.IsWriteLockHeld) throw new Exception("Why?");
        // ...
        root.ww.ExitWriteLock();
    }
}

每个作者都在新线程中开始

class Program{
    public static AVL_Tree data;
    static void Main()
    {
        data = new AVL_Tree();
        List<Thread> vlakna = new List<Thread>();
        for (int i = 1; i < 10; i++)
            vlakna.Add(new Thread(Write));
        foreach (Thread vlakno in vlakna)
            vlakno.Start();
    }
    public static void Write() // Write some random data into the tree
    {
        Random rnd = new Random(DateTime.Now.Millisecond);
        data.Writer(rnd.Next(1, 999));
    }

Writer看起来不完全一样,节点多,代码多,但问题如下:

锁定节点后,锁不会被持有,有时。我不明白为什么。 有人解释一下。

*有时意味着我无法知道它何时会发生。

【问题讨论】:

  • 很明显,将这段代码复制并粘贴到项目中时,它与您的真实代码不同。其中有许多错误会阻止它编译,如果它们以明显的方式修复,则不会引发异常。请尝试创建一个简短但完整的可编译示例来演示该问题。 (您可能会在尝试这样做时自己发现解决方案)
  • 不,这不是真正的代码,现在是代码,但我仍然无法在此处复制整个项目,因为它太长了。这只是对问题的一种解释。
  • 您持有的是写锁,而不是读锁。将测试改为 ww.IsWriteLockHeld
  • 我说的是WriteLock,只是搞错了。是的,这段代码有效。但在我的整个项目中却没有。
  • 同样,如果我们修复了您发布的代码中的编译错误,它不会显示您所描述的问题。我们几乎不可能调试我们没有看到的代码。从一个空项目开始。然后,也许以上述为起点,开始将更多实际代码添加到项目中,直到它真正显示问题。 (然后看看您是否可以删除任何不相关的部分,同时确保它仍然存在问题)

标签: c# .net multithreading synchronization locking


【解决方案1】:

您确定它正在被其他线程访问吗?如果是同一个线程递归获取锁,将授予每个递归级别。

要查看是否是这种情况,请将锁递归策略更改为 NoRecursion 并查看是否出现异常。

[编辑]

这是另一个想法:你有一个竞争条件。

您启动的每个线程都在调用 data.Write(),即 AVL_tree.Write()。

在 AVL_tree.Write() 中分配一个新的根节点。

让我们检查一下您的 AVL_tree.Write():

class AVL_tree
{
    public Node root;
    // ...
    public void Write(int value)
    {
        root = new Node();         // [A]
        root.ww.EnterWriteLock();  // [B]
        if (!root.ww.IsWriteLockHeld) throw new Exception("Why?"); // [C]
        // ...
        root.ww.ExitWriteLock();
    }
}

假设线程 1 已经执行到第 [B] 行,并且即将执行第 [C] 行。

现在假设线程 2 出现并执行行 [A] 并且即将执行行 [B]。

此时,根字段已被一个新字段覆盖,一个尚未获得其写锁的字段。

现在假设线程 1 继续行 [C]。它查看根(现在是线程 2 更新的根),发现写锁没有被持有,因此抛出异常。

这就是我认为正在发生的事情。

【讨论】:

  • 感谢新观点,但我确信这不是问题所在。即使更改了递归策略,问题仍然存在。
  • 非常感谢mutch,那是我犯的那种错误。修复如下:将引用复制到根以编写方法并使用此引用而不是根。
猜你喜欢
  • 1970-01-01
  • 2014-12-22
  • 2018-11-30
  • 2019-08-03
  • 1970-01-01
  • 2011-09-13
  • 2013-10-28
  • 2014-02-28
  • 1970-01-01
相关资源
最近更新 更多