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

C# 为什么移动这条线会导致死锁?,c#,multithreading,deadlock,thread-synchronization,C#,Multithreading,Deadlock,Thread Synchronization,我想我遗漏了一些显而易见的东西: 为什么此代码不会导致死锁: static void Main(string[] args) { object _lock1 = new object(); object _lock2 = new object(); Thread code1 = new Thread(() => { lock (_lock1) { lock (_lock2) {

我想我遗漏了一些显而易见的东西:

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

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");
}
要帮助解释“僵局”问题,请执行以下操作:

如果确实要挂起线程,请使用
Thread.Sleep

但在这种情况下,如果您不想挂起线程,只需挂起“任务”

使用类似的方法:

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");
        }
static void Main(字符串[]args)
{
对于(int i=0;i<1000;i++)
{
对象_lock1=新对象();
对象_lock2=新对象();
线程代码1=新线程(()=>
{
锁(锁1)
{
锁(锁2)
{
控制台。写入线(“A”);
睡眠(100);
}
}
});
线程代码2=新线程(()=>
{
锁(锁2)
{
锁(锁1)
{
控制台。写入线(“B”);
睡眠(100);
}
}
});
代码1.Start();
代码2.Start();
代码1.Join();
代码2.Join();
}
控制台。写入线(“完成”);
}
常规流“假设”线程1首先执行(如果t2恰好在t1之前捕获l2,那么实际上仍然会死锁):

如果t2“碰巧先运行”

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

Start T2
T2 - Locked L2
T2 - Sleeping
Start T1
T1 - Locked L1
--死锁。。除非另一个解锁,否则任何一个都不能继续


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

行为未定义。运行几次可能会得到不同的输出。(“问题:僵局的剖析”)。为什么你的第一个例子并不总是失败?只是因为你运气好(这通常是种族条件下的事情的本质)。
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..
Start T2
T2 - Locked L2
T2 - Sleeping
Start T1
T1 - Locked L1