C# 死锁

最近用C#写服务器时,出现了死锁现象,刚开始但不知道哪里出了问题。
很难排查,直到我写了另一个线程去输出一个数据时(代码中有lock()),然后发现一执行排查线程就阻塞,发现就一直阻塞死锁在:lock()的地方,然后着手学习了一下:C#的死锁、线程同步。

死锁栗子

我们知道一种比较明显的死锁写法:

// 情况1:多个lock对象,互相嵌套lock,很容死锁
// 测试多个lock不同的对象,很容易死锁(这里的代码在CSDN线编辑器写的,可能会有问题)
// author   :   Jave.Lin
// date     :   2018-04-21
public class TestDeadLock {
    private object A = new object();
    private object B = new object();
    public void RunTask1() {
        new Task(()=>{
            // 如果RunTask1会先lock(A)的话
            // 那么RunTask2中执行lock(A)时会阻塞,需要等RunTask1的lock(A)内的代码执行完
            lock(A) {
                // 这里先睡眠1000毫秒,保证RunTask2的lock(B)先执行
                Thread.Sleep(1000);
                lock(B) {
                } // end lock(B)
            } // end lock(A)
        }).Start();
    } // end RunTask1 func

    public void RunTask2() {
        new Task(()=>{
            lock(B) {
                // RunTask1会先lock(A)的话
                // 所以这里lock(A)时会阻塞,需要等这里的RunTask1的lock(A)内的代码执行完
                // 而RunTask1的lock(A)内的代码有lock(B),也在等RunTask2的lock(B)
                // 所以双方互等,无限等而死锁

                // 这里先睡眠1000毫秒,保证RunTask1的lock(A)先执行
                Thread.Sleep(1000);
                lock(A) {
                } // end lock(A)
            } // end lock(B)
        }).Start();
    } // end RunTask2 func

} // end class Definition

/*
简单理解为:
大家互相拿到自己的锁后,这时还想拿对方的锁,就会死锁
*/

再来看另一个死锁的写法
我们总结了上面一个例子的死锁情况,然后我们尽力避免不必要的lock对象,但还死锁了


情况2:每个类下,只有一个lock对象,一样死锁
总结为:不论一类下有多少个:lock对象
如果调用写法含类似结构:
Thread1.Run(()=>{lock(前)、lock(后)})
Thread2.Run(()=>{lock(后)、lock(前)})
那么都挺大几率会死锁,而且,嵌套深度不限,只要有其中一个嵌套深度下有互为嵌套lock关系的,都有可能死锁


using System;
using System.Threading;
using System.Threading.Tasks;

namespace TestDeadLock
{
    class A
    {
        private object AL = new object();
        public void ACall(Action a)
        {
            lock (AL) a();
        }
    }

    class B
    {
        private object BL = new object();
        public void BCall(Action a)
        {
            lock (BL) a();
        }
    }
    /// <summary>
    /// 测试:一个类定义下,只有一个private object locker,一样有死锁的可能
    /// author  :   Jave.Lin
    /// date    :   2018-04-21
    /// </summary>
    class Program
    {
        static object locker = new object();
        static void Main(string[] args)
        {
            var a = new A();
            var b = new B();

            new Task(() =>
            {
                Console.WriteLine("Task1.Started...");
                a.ACall(() =>
                {
                    Console.WriteLine("Task1.a.ACall Entered...");
                    Thread.Sleep(1000);
                    b.BCall(() =>
                    {
                        Console.WriteLine("Task1.b.BCall Entered...");
                        Thread.Sleep(1000);
                        Console.WriteLine("Task1.b.BCall Exit...");
                    });
                    Console.WriteLine("Task1.a.ACall Exit...");
                });
                Console.WriteLine("Task1.Exiting...");
            }).Start();

            new Task(() =>
            {
                Console.WriteLine("Task2.Started...");
                b.BCall(() =>
                {
                    Console.WriteLine("Task2.b.BCall Entered...");
                    a.ACall(() =>
                    {
                        Console.WriteLine("Task2.a.ACall Entered...");
                        Thread.Sleep(1000);
                        Console.WriteLine("Task2.a.ACall Exit...");
                    });
                    Thread.Sleep(1000);
                    Console.WriteLine("Task2.b.BCall Exit...");
                });
                Console.WriteLine("Task2.Exiting...");
            }).Start();

            lock (locker) Monitor.Wait(locker);
        }
    }
}

运行截图:
这里写图片描述

(死锁后程序不会往下执行,就一直阻塞这,卡在这输出,大家可以copy代码到Console项目运行即可看到上图的效果)

如何避免

死锁解决思路

对lock(obj)(就是Monitor.Enter, Monitor.Exit)的死锁解决思路 - 1

猜你喜欢

转载自blog.csdn.net/linjf520/article/details/80030030