锁定已获取并且进一步尝试锁定不会阻塞:C# 锁是可重入的吗?

发布于 2024-10-14 22:11:23 字数 774 浏览 2 评论 0原文

我已经编写了一个我认为应该是有效的死锁案例的测试。看来,一旦 a 类的实例获取了锁,该实例就不再需要重新获取锁,即使我明确尝试调用另一个应该再次锁定的方法。

这是类:

internal class Tester
{
    private readonly object _sync = new object();

    public Tester() { }

    public void TestLock()
    {
        lock (_sync)
        {
            for (int i = 0; i < 10; i++)
            {
                Deadlock(i);
            }
        }

    }

    private void Deadlock(int i)
    {
        lock (_sync)
        {
            Trace.WriteLine(i + " no deadlock!");
        }
    }
}

输出:

0没有死锁!
1 没有死锁!
2 没有死锁!
3 没有死锁!
4 没有死锁!
5 没有死锁!
6 没有死锁!
7 没有死锁!
8 没有死锁!
9 没有死锁!

我本以为这会导致僵局......有人能解释一下吗?

I've written a test of what I think should be a valid case for a deadlock. It appears that once the lock has been acquired by an instance of the a class, that instance doesn't need to re-acquire the lock anymore even if I explicitly try to call another method that should lock again.

Here is the class:

internal class Tester
{
    private readonly object _sync = new object();

    public Tester() { }

    public void TestLock()
    {
        lock (_sync)
        {
            for (int i = 0; i < 10; i++)
            {
                Deadlock(i);
            }
        }

    }

    private void Deadlock(int i)
    {
        lock (_sync)
        {
            Trace.WriteLine(i + " no deadlock!");
        }
    }
}

Output:

0 no deadlock!
1 no deadlock!
2 no deadlock!
3 no deadlock!
4 no deadlock!
5 no deadlock!
6 no deadlock!
7 no deadlock!
8 no deadlock!
9 no deadlock!

I would have thought that this would cause a deadlock... can anybody shed some light on this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

胡渣熟男 2024-10-21 22:11:23

.NET 中的锁是可重入的。仅阻止来自其他线程的获取。当同一个线程多次锁定同一个对象时,它只是增加一个计数器,并在释放时减少它。当计数器达到零时,锁实际上被释放以供其他线程访问。

Locks in .NET are reentrant. Only acquisitions from other threads are blocked. When the same thread locks the same object multiple times, it simply increments a counter, and decrements it when released. When the counter hits zero, the lock is actually released for access from other threads.

蘑菇王子 2024-10-21 22:11:23

Monitor、Mutex 和 ReaderWriterLock 类维护具有线程关联性的锁。 ReaderWriterLockSlim 类允许您选择,它有一个采用 LockRecursionPolicy 值的构造函数。使用 LockRecursionPolicy.NoRecursion 是一种优化,如果您的锁定确实是细粒度的,那么这是一个相当大的优化。

Semaphore 类是一个不具有任何线程关联性的同步类。这段代码可靠地死锁:

class Tester {
    private Semaphore sem = new Semaphore(1, 1);
    public void TestLock() {
        sem.WaitOne();
        for (int i = 0; i < 10; i++) Deadlock(i);
        sem.Release();
    }

    private void Deadlock(int i) {
        if (!sem.WaitOne(100)) Console.WriteLine("deadlock!");
        else {
            sem.Release();
            Console.WriteLine("No deadlock!");
        }
    }
}

一般来说,线程仿射同步类需要两个线程和两个锁才能死锁。标准模式是一个线程获取锁 A 和 B,另一个线程获取锁 B 和 A。顺序很重要。

.NET 编程中存在不太明显的死锁场景,这些死锁场景是由您看不到的锁引起的,因为它们内置于 .NET 框架代码中。一个非常经典的就是BackgroundWorker。您可以在 UI 线程中编写在 Busy 属性上旋转的代码,等待 BGW 完成。当 BGW 有 RunWorkerCompleted 事件处理程序时,总是会出现死锁。在 UI 线程空闲之前它无法运行,在事件处理程序完成运行之前 BGW 的 Busy 属性不会为 false。

The Monitor, Mutex and ReaderWriterLock classes maintain locks that have thread affinity. The ReaderWriterLockSlim class lets you choose, it has a constructor that takes a LockRecursionPolicy value. Using LockRecursionPolicy.NoRecursion is an optimization, a fairly big one if your locking is really fine-grained.

The Semaphore class is a synchronization class that does not have any thread affinity. This code deadlocks reliably:

class Tester {
    private Semaphore sem = new Semaphore(1, 1);
    public void TestLock() {
        sem.WaitOne();
        for (int i = 0; i < 10; i++) Deadlock(i);
        sem.Release();
    }

    private void Deadlock(int i) {
        if (!sem.WaitOne(100)) Console.WriteLine("deadlock!");
        else {
            sem.Release();
            Console.WriteLine("No deadlock!");
        }
    }
}

In general, the thread affine synchronization classes require two threads and two locks to deadlock. The standard pattern is for one thread to acquire locks A and B, for the other to acquire B and A. The order is important.

There are less obvious deadlocks scenarios around in .NET programming, induced by locks that you cannot see because they are built-in to the .NET framework code. A very classic one is for BackgroundWorker. You could write code in the UI thread that spins on the Busy property, waiting for the BGW to complete. That always deadlocks when the BGW has a RunWorkerCompleted event handler. It cannot run until the UI thread goes idle, the BGW's Busy property won't be false until the event handler finished running.

漫漫岁月 2024-10-21 22:11:23

在您的场景中,您有一个锁在另一个锁中。一旦代码命中“Deadlock”中的嵌套锁,“lock(...)”代码本质上会被忽略,因为它已经在“TestLock”中获取了它。

线程的重要来源:http://www.albahari.com/threading/part2.aspx

In your scenario, you have a lock within another lock. Once the code hits the nested lock in "Deadlock", the "lock(...)" code is essentially ignored because it has already acquired it in "TestLock".

Great source for threading: http://www.albahari.com/threading/part2.aspx.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文