为什么移动此行会导致死锁?

时间:2018-11-06 03:42:41

标签: 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)
            {
                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");
}

3 个答案:

答案 0 :(得分:2)

两个代码段都可能导致死锁,应避免使用。只是一个巧合,第一个片段没有陷入僵局。在锁之间添加一些操作会增加获得死锁的可能性。 例如,如果在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");
        }

答案 1 :(得分:1)

常规流“假定”线程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“首先发生”。

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

-死锁。除非对方解锁,否则任何一个都无法继续。

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

答案 2 :(得分:0)

为帮助解释“僵局”问题-

如果您确实想暂停线程,请使用Thread.Sleep

但是在这种情况下,您不想挂起线程,只需挂起“任务”即可。

使用类似这样的内容:

await Task.Delay(myDuration);

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

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