【问题标题】:Why moving this line cause deadlock?为什么移动这条线会导致死锁?
【发布时间】:2018-11-06 03:42:41
【问题描述】:

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

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

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");
}

【问题讨论】:

标签: c# multithreading deadlock thread-synchronization


【解决方案1】:

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

        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");
        }

【讨论】:

    【解决方案2】:

    常规流程“假设”线程 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
    

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

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

    【讨论】:

      【解决方案3】:

      帮助解释“死锁”问题 -

      如果您真的想暂停线程,请使用Thread.Sleep

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

      像这样使用一些:

      await Task.Delay(myDuration);
      

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

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

      【讨论】:

        猜你喜欢
        • 2019-12-27
        • 2012-08-27
        • 2012-01-16
        • 2015-03-09
        • 1970-01-01
        • 2020-08-20
        • 2021-07-13
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多