《Effective Objective-C 2.0》中“第46条:不要使用dispatch_get_current_queue”笔记
我们都知道,在串行队列的同步任务中,再次向其中派发同步任务会造成死锁(向主队列派发同步任务的死锁就是这样)。但是,如果是下面这样呢?
dispatch_queue_t queueA = dispatch_queue_create("com.jiji.serialQueueA", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t queueB = dispatch_queue_create("com.jiji.serialQueueB", DISPATCH_QUEUE_SERIAL);
dispatch_sync(queueA, ^{
// 向queueB中派发同步任务
dispatch_sync(queueB, ^{
// 向queueA中派发同步任务
dispatch_sync(queueA, ^{
NSLog(@"inner----");
});
});
NSLog(@"outer-=-=-=-=-=-=");
});
结果同样死锁。这是因为最内部派发到queueA中的任务需要按队列的方式,等待先入队的最外层任务执行完成,但最外层任务被最内部的任务阻塞无法继续执行,故导致死锁。这在道理上与最简单的死锁没有区别。
但是,当我们想要使用dispatch_get_current_queue函数,对内部执行队列进行区分,防止死锁时,可以发现此方法早已被标记为了deprecated。也就是说,此函数返回的队列不能作为任务执行队列的判断依据。
故数组建议我们使用dispatch_queue_set_specific函数,对队列绑定相关数据。在派发的任务执行时,动态取出绑定数据来判定当前的执行队列**。
直接看对比示例:
// 将queueB的目标队列设置为queueA,即queueB中的任务被派发到queueA中执行
dispatch_set_target_queue(queueB, queueA);
// 给指定队列绑定相关数据(使用键值对的方式)
static int kQueueSpecific;
CFStringRef queueSpecificValue = CFSTR("queueA");
dispatch_queue_set_specific(queueA,
&kQueueSpecific,
(void *)queueSpecificValue,
(dispatch_function_t)CFRelease);
dispatch_sync(queueB, ^{
dispatch_block_t block = ^ {
NSLog(@"hahahahaha~~");
};
dispatch_queue_t currentQueue = dispatch_get_current_queue();
if (currentQueue == queueA) {
NSLog(@"currentQueue == queueA");
} else {
NSLog(@"currentQueue == queueB");
}
// 在当前队列中取出绑定的相关数据
CFStringRef retrievedValue = dispatch_get_specific(&kQueueSpecific);
if (retrievedValue) {
// 存在相关值,即当前就在绑定的队列中执行,不要再向那个队列派发同步任务,会死锁
NSLog(@"in queueA");
block();
} else {
// 不存在,证明执行不在指定的那个队列中,可以派发同步任务
NSLog(@"NOT in queueA");
dispatch_sync(queueA, block);
}
});
NSLog(@"finish!!!!");
执行结果:
LockTest[19151:825723] currentQueue == queueB
LockTest[19151:825723] in queueA
LockTest[19151:825723] hahahahaha~~
LockTest[19151:825723] finish!!!!
可以看出,由于设置了目标队列,实际上是有queueA执行派发任务。但是dispatch_get_current_queue只能反映出原始派发任务的队列,而通过dispatch_get_specific函数取到的绑定数据却是根据真实执行任务的队列获取到。故在同步派发任务时,为了防止死锁,可以使用dispatch_queue_set_specific和dispatch_get_specific配对,来识别当前执行任务的队列**。
这就是不要使用dispatch_get_current_queue的原因。