平时我们可能很少用到 dispatch_sync ,只是知道,如果调用 dispatch_sync(dispatch_get_main_queue(), block) 的话,会阻塞主线程。但事实并非如此。
另外,dispatch_sync() 和 dispatch_async() 可不仅仅是差了一个字母,也不仅仅是同步和异步的差别。
在看之前,建议首先要明确,队列、线程、任务,的关系。我发现很多人会把队列和线程搞混。一定要注意!
首先,dispatch_sync 的作用是,往某队列(第一个参数)中,添加一个同步任务(第二个参数)。
那么我们通常说其会造成主线程死锁的情况,比如场景0会造成死锁的情况。这大家都知道,就不多说了。
为了方便下面的讲解,先贴上官方文档的一个解释:
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()).
下面举例说明:
(PS: queue 和 queue2 是 serial 还是 concurrent 对本例影响不大)
场景0:
1. (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)执行 ;第二个任务在主线程中执行,官方文档解释:除非第一个参数你传入了主队列
- (void)viewDidLoad {
[super viewDidLoad];
//队列1
dispatch_queue_t queue = dispatch_queue_create("com.gcd.serial", DISPATCH_QUEUE_SERIAL);
//队列2
dispatch_queue_t queue2 = dispatch_queue_create("com.gcd.concurrent", DISPATCH_QUEUE_CONCURRENT);
//主线程中,往 queue 添加一个异步任务,是异步任务,不会等待。
dispatch_async(queue, ^{
NSLog(@"异步任务在 %@" 执行, [NSThread currentThread]);
//这里是子线程,比如 number = 3
//在子线程(number = 3),往 queue2 添加一个同步任务,此时子线程(number = 3)会立即等待,即被阻塞,等待block执行完毕
//添加一个同步任务到queue2中
dispatch_sync(queue2, ^{
//这个任务也会在number = 3 的子线程中执行
NSLog(@"同步任务在 %@ 执行", [NSThread currentThread]);
});
//同步任务执行完毕,继续执行异步任务
NSLog(@"异步任务完成!")
});
}