46、不要使用dispatch_get_current_queue
- (NSString *)name {
__block NSString *localName;
// 如果调用方法的队列恰好使_syncQueue,则会死锁 (同步线程里加同步事件)
dispatch_sync(_syncQueue, ^{
localName = _name;
});
return localName;
}dispatch_queue_t queueA = dispatch_queue_create("com.mo.queueA", NULL);
dispatch_queue_t queueB = dispatch_queue_create("com.mo.queueB", NULL);
dispatch_sync(queueA, ^{
dispatch_sync(queueB, ^{
dispatch_block_t block ^{ /* ... */ };
if (dispatch_get_current_queue() == queueA) { // dispatch_get_current_queue 返回的是queueB
block();
} else {
dispatch_sync(queueA, block); // 所以还是会进入死锁
}
});
}); 由于队列间有层级关系,所以“检查当前队列是否为执行同步派发所用的队列”这种办法,并不总是凑效。
在这种情况下,正确的做法是:不要把存取方法做成可重入的,而是应该确保同步操作所有的队列,绝不会访问属性,也就是绝对不会调用name方法。这种队列只应该用来同步属性。由于派发队列是一种极为轻量的机制,所以不妨为每个每项属性创建专用的同步队列。
GCD提供了一个功能,设定“队列特有数据”,可以把任意数据以键值对的形式关联到队列里。最重要的是,若在当前层级获取不到关联数据时,系统会沿着层级体系向上查找,直到 找到数据 / 到根队列 为止。
dispatch_queue_t queueA = dispatch_queue_create("com.mo.queueA", NULL);
dispatch_queue_t queueB = dispatch_queue_create("com.mo.queueB", NULL);
dispatch_set_target_queue(queueB, queueA); // B嵌套在A里
static int kQueueSpecific;
CFStringRef queueSpecificValue = CFSTR("queueA");
// 为queueA设置“队列特定值”
dispatch_queue_set_specific(queueA, // 待设置数据的队列
&kQueueSpecific, // key
(void *)queueSpecificValue, // value
(dispatch_funcion_t)CFRelease); // 析构方法
dispatch_sync(queueB, ^{
dispatch_block_t block = ^{
NSLog(@"No deadlock!");
};
CFStringRef retrievedValue = dispatch_get_specific(&kQueueSpecific); // 获取队列特定值
if (retrievedValue) { // 在A队列里
block();
} else {
dispatch_sync(queueA, block);
}
});要点:
dispatch_get_current_queue函数的行为常常与开发者所预期的不同。此函数已经废弃,只应做调试之用由于派发队列是按层级来组织的,所以无法单用某个队列对象来描述“当前队列”这一概念
Dispatch_get_current_queue函数用于解决由不可重入的代码所引发的死锁,然而能用此函数解决的问题,通常该用“队列特定数据”来解决
Last updated
Was this helpful?