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之后才出现的方法,他的底层实现是让线程睡眠,线程睡眠那么就不会出现假的死锁状态,所以就不会出现优先级反转了。