队列:负责调度任务
串行队列(Serial Dispatch Queue):一个接一个的调度任务,要等待上一个执行完,再执行下一个。
并行队列(Concurrent Dispatch Queue):可以同时调度多个任务,不需要上一个执行完,就能执行下一个
串行和并行针对的是队列
主队列在主线程中,所以在主队列中的任务一定是在主线程中执行的。但是主线程中可以有其他队列。既可以有串行队列也可以有并行队列。
线程 (同步和异步针对的是线程)
同步线程
阻塞当前线程,必须要等待同步线程中的任务执行完才能返回。
dispatch_sync(<#dispatch_queue_t _Nonnull queue#>, <#^(void)block#>)
注意:
来自Apple 官方文档
Submits a block to the specified dispatch queue for synchronous execution. Unlike dispatch_async, this function does not return until the block has finished
.
Calling this function and targeting the current queue results in deadlock
.
Unlike with dispatch_async, no retain is performed on the target queue. Because calls to this function are synchronous, it "borrows" the reference of the caller. Moreover, no Block_copy is performed on the block.
As a performance optimization, this function executes blocks on the current thread whenever possible, with one obvious exception. Specifically, blocks submitted to the main dispatch queue always run on the main thread.
异步线程
不阻塞当前线程
This function is the fundamental mechanism for submitting blocks to a dispatch queue. Calls to this function always return immediately after the block has been submitted and never wait for the block to be invoked
. The target queue determines whether the block is invoked serially or concurrently with respect to other blocks submitted to that same queue. Independent serial queues are processed concurrently with respect to each other.
GCD 导致死锁的2个案例
1、同步
主线程
主队列
导致死锁
- (void)viewDidLoad {
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"死锁");
});
}
修改为一下代码,可解决死锁:
- (void)viewDidLoad {
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"解决死锁");
});
}
导致死锁的原因:
首先,主线程中默认有一个主队列,viewDidLoad作为任务1,被放入了主队列,当执行同步线程时,block作为任务2,也被放入队列中,因为同步线程的一个特点:(Unlike dispatch_async, this function does not return until the block has finished),所以阻塞当前线程,并立马执行任务2。因为当前主队列中有两个任务(任务1(viewDidLoad) 和 任务2(block) ) ,并且任务1在队列的顶端,必须执行完毕,从队列出来才能执行任务2。任务1是不可能执行完成的,因为任务2是任务1的一部分,所以任务1不可能完成,也就导致任务1在顶端无法从队列中出来,任务2就卡在了队列中,也就无法执行完成。导致死锁。
解决死锁的原因:
dispatch_async 不会阻塞线程,无需等待block立即执行,就返回。所以任务1可以从队列中弹出,然后执行任务2.
2、异步线程
中同步
将任务加入当前队列
- (void)viewDidLoad {
dispatch_queue_t queue = dispatch_queue_create("serrial", DISPATCH_QUEUE_SERIAL);
NSLog(@"打印1");
dispatch_async(queue, ^{ // 任务1
NSLog(@"打印2");
dispatch_sync(queue, ^{ // 任务2
NSLog(@"死锁");
});
});
NSLog(@"打印3");
}
打印1
打印3
打印2
死锁
解决方式1、
- (void)viewDidLoad {
dispatch_queue_t queue = dispatch_queue_create("serrial", DISPATCH_QUEUE_SERIAL);
NSLog(@"打印1");
dispatch_async(queue, ^{
NSLog(@"打印2");
dispatch_async(queue, ^{
NSLog(@"解决死锁");
});
});
NSLog(@"打印3");
}
解决方式2:
- (void)viewDidLoad {
dispatch_queue_t queue = dispatch_queue_create("serrial", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t queue1 = dispatch_queue_create("serrial", DISPATCH_QUEUE_SERIAL);
NSLog(@"打印1");
dispatch_async(queue, ^{
NSLog(@"打印2");
dispatch_sync(queue1, ^{
NSLog(@"解决死锁");
});
});
NSLog(@"打印3");
}
解决方式3:
- (void)viewDidLoad {
dispatch_queue_t queue = dispatch_queue_create("serrial", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"打印1");
dispatch_async(queue, ^{
NSLog(@"打印2");
dispatch_sync(queue, ^{
NSLog(@"解决死锁");
});
});
NSLog(@"打印3");
}
死锁的原因
当前存在两个队列(主队列和queue),两个线程(主线程 子线程)。
打印1 和 打印4 是在主线程中执行的。打印2 是在子线程中执行的。
打印1执行完毕后,子线程开辟,打印2和打印4无先后。任务1(block1)加入queue队列,打印2,任务2(block2)加入队列,因为当前线程为同步线程,会阻塞当前线程,等到任务2的block执行完毕,才会往下执行结束任务1。这个时候队列是什么情况呢?队列里有两个任务,任务1和任务2,并且因为任务1是先进入队列的,根据FIFO原则,只有任务1执行完毕,任务1才能弹出队列,执行任务2,但是任务1要想执行完毕,就得执行任务2,因为任务2是任务1的一部分。这个时候的任务2在队列末尾等着 任务1滚出去,来完成任务。但是任务1又因为任务2在队列里等自己,所以任务1也无法完成。导致死锁。
总结
Apple 官方文档摘录:
Calling this function and targeting the current queue results in deadlock.
使用sync
函数往当前串行队列
中添加任务,会卡住当前的串行队列(产生死锁)
代码解释:
死锁:
dispatch_queue_create("serrial", DISPATCH_QUEUE_SERIAL);
NSLog(@"打印1");
dispatch_async(queue, ^{
NSLog(@"打印2");
dispatch_sync(queue, ^{ // sync 函数往所在的queue中添加任务
NSLog(@"死锁");
});
});