为什么移动这条线会导致死锁?

Why moving this line cause deadlock?

我想我在这里遗漏了一些明显的东西:

为什么这段代码不会导致死锁:

static void Main(string[] args)
{
    object _lock1 = new object();
    object _lock2 = new object();

    Thread code1 = new Thread(() =>
    {
        lock (_lock1)
        {
            lock (_lock2)
            {
                Console.WriteLine("A");
                Thread.Sleep(3000);
            }
        }
    });

    Thread code2 = new Thread(() =>
    {
        lock (_lock2)
        {
            lock (_lock1)
            {
                Console.WriteLine("B");
                Thread.Sleep(3000);
            }
        }
    });

    code1.Start();
    code2.Start();

    code1.Join();
    code2.Join();

    Console.WriteLine("Done");
}

但是这个确实如此:

static void Main(string[] args)
{
    object _lock1 = new object();
    object _lock2 = new object();

    Thread code1 = new Thread(() =>
    {
        lock (_lock1)
        {
            lock (_lock2)
            {
                Thread.Sleep(3000);
                Console.WriteLine("A");
            }
        }
    });

    Thread code2 = new Thread(() =>
    {
        lock (_lock2)
        {
            Thread.Sleep(3000);
            lock (_lock1)
            {
                Console.WriteLine("B");
            }
        }
    });

    code1.Start();
    code2.Start();

    code1.Join();
    code2.Join();

    Console.WriteLine("Done");
}

帮助解释 "deadlock" 问题 -

如果你真的想挂起线程,请使用Thread.Sleep

但是在这种情况下你不想挂起线程,只是挂起 'task'.

像这样使用:

await Task.Delay(myDuration);

这不会挂起整个线程,而只会挂起您要挂起的单个任务。

同一线程上的所有其他任务可以继续运行...

这两个代码片段都可能导致死锁,应该避免。这只是一个巧合,第一个片段没有陷入僵局。在锁之间添加一些操作会增加获得死锁的可能性。 例如,如果在 lock1 和 lock2 之间添加 Console.Writeline,它也会增加死锁的可能性。 您可以 运行 循环中的第一个片段并收到死锁。例如,此代码在一段时间后陷入死锁:

        static void Main(string[] args)
        {
            for (int i = 0; i < 1000; i++)
            {
                object _lock1 = new object();
                object _lock2 = new object();

                Thread code1 = new Thread(() =>
                {
                    lock (_lock1)
                    {
                        lock (_lock2)
                        {
                            Console.WriteLine("A");
                            Thread.Sleep(100);
                        }
                    }
                });

                Thread code2 = new Thread(() =>
                {
                    lock (_lock2)
                    {
                        lock (_lock1)
                        {
                            Console.WriteLine("B");
                            Thread.Sleep(100);
                        }
                    }
                });

                code1.Start();
                code2.Start();

                code1.Join();
                code2.Join();
            }
            Console.WriteLine("Done");
        }

常规流程"assuming"线程 1 首先执行(如果 t2 碰巧在 t1 之前捕获 l2,这实际上仍然会死锁):

t1 acquires l1
t1 acquires l2 //before t2..
t1 unlocks l2
t1 unlocks l1

t2 acquires l2
t2 sleeps
t2 acquires l1
t2 unlocks l1
t2 unlocks l2

如果 t2 "happens to run first"..

t2 acquires l2
t2 sleeps
t1 acquires l1 (while t2 is still sleeping)
t1 tries to acquire l2 but blocks.. it's already acquired by t2..
t2 is finished sleeping..
t2 tries to acquire l1 but blocks.. it's already acquired by t1..

IE:您可以调试并看到它打印如下:

Start T2
T2 - Locked L2
T2 - Sleeping
Start T1
T1 - Locked L1

-- 死锁..除非对方解锁,否则双方都无法继续..

这是未定义的行为。您也不应该猜测哪个线程先运行。