平时我们可能很少用到 dispatch_sync ,只是知道,如果调用 dispatch_sync(dispatch_get_main_queue(), block) 的话,会阻塞主线程。但事实并非如此。另外,dispatch_sync() 和 dispatch_async() 可不仅仅是差了一个字母,也不仅仅是同步和异步的差别。
在看之前,建议首先要明确,队列、线程、任务,的关系。我发现很多人会把队列和线程搞混。一定要注意!
首先,dispatch_sync 的作用是,往某队列(第一个参数)中,添加一个同步任务(第二个参数)。
官方文档的解释如下:
As an optimization, dispatch_sync() invokes the workitem on the thread which
submitted the workitem, except when the passed queue is the main queue or a queue targetting it (See dispatch_queue_main_t, dispatch_set_target_queue()).
作为优化,dispatch_sync() 执行它的任务时,会优先选择添加这个任务的线程,除非第一个参数你传入了主队列或者指定了目标队列
下面举例说明:
(PS: queue 和 queue2 是 serial 还是 concurrent 对本例影响不大)
场景0:
(void)viewDidLoad {
[super viewDidLoad];
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"同步任务在 %@ 执行", [NSThread currentThread]);
});
}
在场景0中,是在主线程中,往主队列添加一个同步任务,结果就造成死锁,主线程被阻塞。是相互等待的情况。
- 主线程正在执行主队列的任务。
- 一个同步任务过来了
- 于是,主线程立即等待
- 同步任务在等主线程当前任务完成,然而主线程在等待这个同步任务执行。
于是就造成了相互等待的情况。
场景1:
- (void)viewDidLoad {
[super viewDidLoad];
dispatch_queue_t queue = dispatch_queue_create("com.gcd.serial", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
NSLog(@"异步任务在 %@ 执行", [NSThread currentThread]);
dispatch_sync(queue, ^{
NSLog(@"同步任务在 %@ 执行", [NSThread currentThread]);
});
});
}
在场景1中,首先是在主线程中,往 queue 添加一个异步任务;然后子线程中,又往queue添加一个同步任务,就和场景0的情况是一样的,会造成死锁。
场景2:
- (void)viewDidLoad {
[super viewDidLoad];
dispatch_queue_t queue = dispatch_queue_create("com.gcd.serial", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t queue2 = dispatch_queue_create("com.gcd.concurrent", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"任务", [NSThread currentThread]);
dispatch_sync(queue2, ^{
NSLog(@"同步任务%@", [NSThread currentThread]);
});
});
}
在场景2中,首先,是在主线程中,往queue添加一个异步任务;然后在子线程中,往queue2添加一个同步任务。这种情况不会死锁,可以正常运行。
最终,这两个任务都会在同一个线程中执行,假如第一个 NSLog 打印线程 number = 3,那么第二个NSLog打印的也一定是3,因为会优先选择添加这个任务的线程
场景3:
- (void)viewDidLoad {
[super viewDidLoad];
dispatch_queue_t queue = dispatch_queue_create("com.gcd.serial", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
NSLog(@"任务", [NSThread currentThread]);
//这里要注意了,并不会死锁!
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"同步任务%@", [NSThread currentThread]);
});
});
}
首先,是在主线程中,往queue添加一个异步任务;然后在子线程中,往主队列添加一个同步任务。这种情况不会死锁,可以正常运行。
最终,第一个任务在子线程(比如number = 3)执行 ;第二个任务在主线程中执行,官方文档解释:除非第一个参数你传入了主队列。
总结
1. 队列中的任务,最终会由线程调度,GCD管理线程的生命周期。
2. dispatch_sync() 是线程同步操作,其作用就是阻塞线程,先让它的block执行完毕,才会返回。
3. dispatch_sync() 造成死锁的条件是:如果在同一个队列(不管是不是主队列)添加同步任务,就会死锁,反之则不会。