iOS 死锁

产生死锁的原因主要是:

(1) 因为系统资源不足。

(2) 进程运行推进的顺序不合适。

(3) 资源分配不当等。

如果系统资源充足,进程的资源请求都能够得到满足,死锁出现的可能性就很低,否则

就会因争夺有限的资源而陷入死锁。其次,进程运行推进顺序与速度不同,也可能产生死锁。

 

产生死锁的四个必要条件:

(1) 互斥条件:一个资源每次只能被一个进程使用。

(2) 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。

(3) 不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。

(4) 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。

这四个条件是死锁的必要条件,只要系统发生死锁,这些条件必然成立,而只要上述条件之

一不满足,就不会发生死锁。

 

死锁的解除与预防:

理解了死锁的原因,尤其是产生死锁的四个必要条件,就可以最大可能地避免、预防和

解除死锁。所以,在系统设计、进程调度等方面注意如何不让这四个必要条件成立,如何确

定资源的合理分配算法,避免进程永久占据系统资源。此外,也要防止进程在处于等待状态

的情况下占用资源。因此,对资源的分配要给予合理的规划。

 

GCD的死锁

1、以下代码是在主线程执行的,会不会产生死锁。

NSLog(@"执行任务1");
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_sync(queue, ^{
NSLog(@"执行任务2");
});

主线程里面同步死锁

 

2、新串行队列里异步里面放同步,死锁

dispatch_queue_t queue = dispatch_queue_create("com.demo.serialQueue", DISPATCH_QUEUE_SERIAL);
NSLog(@"1"); // 任务1
dispatch_async(queue, ^{
    NSLog(@"2"); // 任务2
    dispatch_sync(queue, ^{  
        NSLog(@"3"); // 任务3
    });
    NSLog(@"4"); // 任务4
});
NSLog(@"5"); // 任务5

控制台输出结果:

1

5

2

在新的一个队列里面。任务3所在的同步线程会阻塞,所以任务4必须等任务3执行完以后再执行。这就又陷入了无限的等待中,造成死锁。

 

3、新串行队列里面,同步里面放同步,死锁

dispatch_queue_t aSerialDispatchQueue = dispatch_queue_create("com.test.deadlock.queue", DISPATCH_QUEUE_SERIAL);
    NSLog(@"1"); //任务1
dispatch_sync(aSerialDispatchQueue, ^{
        NSLog(@"2"); //任务2
        dispatch_sync(aSerialDispatchQueue, ^{
            NSLog(@"3"); //任务3
        });
        NSLog(@"4");  //任务4
 });
    NSLog(@"5");  //任务5

值输出1 和2 ;从控制台输出结果来看,执行到任务2后,就已经死锁了。因为该例子中两个GCD都是使用的同步方式,而且还是同一个串行队列,这就导致了和上一个例子一样,自己在等待自己的情况,形成了死锁

 

4、主线程执行异步里面循环,死锁。

NSLog(@"1");
    dispatch_async(dispatch_get_main_queue(), ^{
        while (1) {
            NSLog(@"2");
        }
    });
    dispatch_async(dispatch_get_main_queue(), ^{
        while (1) {
            NSLog(@"3");
        }
    });
    NSLog(@"4");

只输出1和4 和2。 输出的2在主线程里面一直循环,死锁了。

 

5、信号量阻塞主线程,死锁。

NSLog(@"semaphore create!");
    dispatch_async(dispatch_get_main_queue(), ^{
        dispatch_semaphore_signal(semaphore);
        NSLog(@"semaphore plus 1");
    });  
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"semaphore minus 1");

因为dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER)阻塞了当前线程,而且等待时间是DISPATCH_TIME_FOREVER——永远等待,这样它就永远的阻塞了当前线程——主线程。导致主线中的dispatch_semaphore_signal(semaphore)没有执行,

而dispatch_semaphore_wait一直在等待dispatch_semaphore_signal改变信号量,这样就形成了死锁

 

6、主线程死锁

while (1) {
        NSLog(@"1");
    }
    NSLog(@"2");

无限循环

 

7、iOS10之前自旋锁死锁。

-(void)saleTicket{
    OSSpinLockLock(&_lock);
    NSInteger currentTickets = self.ticketsCount;
    currentTickets --;
    self.ticketsCount = currentTickets;
    NSLog(@"当前剩余的票数为:%zd",currentTickets);
    OSSpinLockUnlock(&_lock);
}
#import <libkern/OSAtomic.h>
@property (assign, nonatomic) OSSpinLock lock;

实际上这把锁是不安全的,而且我们在写代码的时候发现苹果已经提示过期不建议使用,为什么不安全呢,因为有可能会造成优先级反转的问题,先说一下什么是优先级反转:比如说我们有3个线程,线程1优先级高,线程2优先级低,优先级高cpu分配资源多,在线程1执行的时间有多些,那么线程1被执行的概率就大,反之线程2执行的概率就小点。假设我们在代码使用自旋锁枷锁的地方先执行的是线程2,或者说线程2先进去,那么枷锁了,因为cpu分配给线程2的资源少,那么就有可能他执行不到解锁的代码,若果一把锁解不开,那么线程1就会一直执行到加锁的代码块那,因为锁没有解开,所以就一直在那运行着,因为自旋锁是一个处于“忙等”的状态,类似于while循环在那。所以会造成一个假的死锁状态。所以说我说这种自旋锁是不安全的。解决办法是是用苹果最新的锁,就是在ios10之后才出现的方法,他的底层实现是让线程睡眠,线程睡眠那么就不会出现假的死锁状态,所以就不会出现优先级反转了。