导读 | 死锁 是指两个或两个以上的进程(线程)在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁 状态或系统产生了死锁,这些永远在互相等待的进程称为死锁 进程(线程)。 |
通常的死锁的示例都是两个锁,多个资源导致的死锁,你有没有想过一个资源也会导致死锁,如何使用一个锁造成死锁呢?思考一下再看下面的代码:
private static readonly object Lock = new object(); public static void Test() { lock (Lock) { Task.Run(TestMethod1).Wait(); } } private static void TestMethod1() { lock (Lock) { Console.WriteLine("xxx"); } }
在 Test 这个方法中首先获取锁,获取锁成功之后调用另外一个线程去调用 TestMethod1 方法,而 TestMethod1 方法中会再次尝试获取锁,此时因为锁已经被 Test 方法获取而且并没有释放,所以会一直获取不到锁从而造成死锁。
其实这种情况还有很多变形,比如说 lock(this)/lock(“lockedString”) 这种都是比较危险的,所以不推荐使用,我们使用上面的示例做一个变形,使用 lock(“lockedString”) 来测试一下。
public static void Test() { lock ("Lock") { Task.Run(TestMethod1).Wait(); } } private static void TestMethod1() { lock ("Lock") { Console.WriteLine("xxx"); } }
这样也会造成死锁,因为 lock 的 string 实际上是同一个引用,字符串池(string intern),所以类似于上面的示例,相当于是一个锁,对于 lock(this) 也是类似的,所以通常 lock 是不推荐 lock(this)/lock(“string”) 这些写法的,对于不同的资源要使用不同的 lock,这样就可以避免上面这个示例的这种情况。
在 SQL Server 中会有一个独立的死锁检测的进程,如果发生死锁的情况,会有一个事务会被选择为牺牲品来解决死锁的问题。
在通过 Redis 实现分布式锁的时候,通常会指定一个锁的过期时间,过期时间通常是为了避免获取锁成功的系统突然宕机导致锁一直在锁定状态,从而导致其他服务获取锁的时候一直获取失败,除此之外,通常还会指定一个最大等待时间,如果别的服务获取到锁了,正在操作,那么会等待锁释放,但是为了避免死锁,如果长时间获取不到锁的话就会放弃获取锁,直接返回获取锁失败。
原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/124848.html