首先说一下线程锁死的概念
一个比较常见的死锁例子:主队列同步
- (void)viewDidLoad {
[super viewDidLoad];
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"deallock");
});
}
同步对于任务是立刻执行的,那么当把任务放进主队列时,它就会立马执行,只有执行完这个任务,viewDidLoad才会继续向下执行。
而viewDidLoad和任务都是在主队列上的,由于队列的先进先出原则,任务又需等待viewDidLoad执行完毕后才能继续执行,viewDidLoad和这个任务就形成了相互循环等待,就造成了死锁。
想避免这种死锁,可以将同步改成异步dispatch_async
,或者将dispatch_get_main_queue
换成其他串行或并行队列,都可以解决。
1 @synchronized 互斥锁
特点:
①多个线程访问时锁住的对象必须是同一个 一般使用self
②锁住的代码尽量少
③会隐式的添加一种异常处理机制,当出现死锁的情况下会自动释放锁,不会出现线程死锁
④比较消耗性能
@synchronized (锁住的对象-一般使用self) {
锁住的代码块
}
2.NSlock
分为:NSLock
、NSConditionLock
、NSRecursiveLock
、NSCondition都遵循NSLocking协议
NSLock:
①有lock方法和unlock方法,必须成对出现否则会出现死锁现象
②tryLock 和
lockBeforeDate 这两个方法都是尝试加锁,加锁失败了会返回no,不会使线程锁死
③不能递归调用否则会出现锁死
NSRecursiveLock
①可以递归调用,因为它会记录lock的次数和unlock的次数,当lock和unlock的次数达到平衡时就会释放锁
NSConditionLock
①
定义的互斥锁可以在使得在某个条件下进行锁定和解锁
②NSConditionLock
是一种特殊类型的锁,通过它可以实现不同线程的调度。
一个线程被某一个条件所阻塞,直到另一个线程满足该条件从而发送信号给该线程使得该线程可以正确的执行
用代码举例说明
- (void)nsconditionlock {
NSConditionLock * cjlock = [[NSConditionLock alloc] initWithCondition:0];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[cjlock lock];
NSLog(@"线程1加锁成功");
sleep(1);
[cjlock unlock];
NSLog(@"线程1解锁成功");
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(1);
[cjlock lockWhenCondition:1];
NSLog(@"线程2加锁成功");
[cjlock unlock];
NSLog(@"线程2解锁成功");
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(2);
if ([cjlock tryLockWhenCondition:0]) {
NSLog(@"线程3加锁成功");
sleep(2);
[cjlock unlockWithCondition:2];
NSLog(@"线程3解锁成功");
} else {
NSLog(@"线程3尝试加锁失败");
}
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
if ([cjlock lockWhenCondition:2 beforeDate:[NSDate dateWithTimeIntervalSinceNow:10]]) {
NSLog(@"线程4加锁成功");
[cjlock unlockWithCondition:1];
NSLog(@"线程4解锁成功");
} else {
NSLog(@"线程4尝试加锁失败");
}
});
}
- 在线程 1 解锁成功之后,线程 2 并没有加锁成功,而是继续等了 1 秒之后线程 3 加锁成功,这是因为线程 2 的加锁条件不满足,初始化时候的 condition 参数为 0,而线程 2
- 加锁条件是 condition 为 1,所以线程 2 加锁失败。
- lockWhenCondition 与 lock 方法类似,加锁失败会阻塞线程,所以线程 2 会被阻塞着。
- tryLockWhenCondition: 方法就算条件不满足,也会返回 NO,不会阻塞当前线程。
- lockWhenCondition:beforeDate:方法会在约定的时间内一直等待 condition 变为 2,并阻塞当前线程,直到超时后返回 NO。
- 锁定和解锁的调用可以随意组合,也就是说 lock、lockWhenCondition:与unlock、unlockWithCondition: 是可以按照自己的需求随意组合的
dispatch_semaphore 相当于同步锁
①是 GCD 用来同步的一种方式,与他相关的只有三个函数,一个是创建信号量,一个是等待信号,一个是发送信号
②机制就是当有多个线程进行访问的时候,只要有一个获得了信号,其他线程的就必须等待该信号释放。
dispatch_semaphore还可以控制GCD 中一个队列中有多个任务时,一次最多只能执行几个
比如一个队列中有三个任务,但每次最多只能执行两个任务
举例如下:
-(void)dispatchSignal{
//crate的value表示,最多几个资源可访问
dispatch_semaphore_t semaphore = dispatch_semaphore_create(2);
dispatch_queue_t quene = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//任务1
dispatch_async(quene, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"run task 1");
sleep(1);
NSLog(@"complete task 1");
dispatch_semaphore_signal(semaphore);
});<br>
//任务2
dispatch_async(quene, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"run task 2");
sleep(1);
NSLog(@"complete task 2");
dispatch_semaphore_signal(semaphore);
});<br>
//任务3
dispatch_async(quene, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"run task 3");
sleep(1);
NSLog(@"complete task 3");
dispatch_semaphore_signal(semaphore);
});
}