判断当前队列的方式
- 使用
dispatch_get_current_queue
获取当前执行的队列 - 使用
dispatch_queue_set_specific & dispatch_get_specific
标标识获取指定队列 - 使用dispatch_queue_get_label获取队列标签,比较字符串判断。
注意: dispatch_get_current_queue
在iOS6
之后是弃用的,苹果只推荐在打印中使用,原因是容易导致死锁。会在下面单独展示死锁分析。
使用dispatch_queue_set_specific
来判断当前队列
实现方式
// 1. 创建一个标识
static void * mainQueueKey = &mainQueueKey;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// 2. 将标识关联到指定队列上
dispatch_queue_set_specific(dispatch_get_global_queue(0, 0), mainQueueKey, mainQueueKey, NULL);
});
// 3. 在需要的时机通过以下方法判断,返回context Data
dispatch_get_specific(mainQueueKey)
定义一个方法判断是否为主队列
BOOL isMainQueue() {
static void * mainQueueKey = &mainQueueKey;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
dispatch_queue_set_specific(dispatch_get_main_queue(), mainQueueKey, mainQueueKey, NULL);
});
return dispatch_get_specific(mainQueueKey) == mainQueueKey;
}
API
解释
向queue
这个队列中设置一个key
,其对应的数据为context
。那么当我们想判断一个代码块是否被这个queue
队列执行时,就可以通过dispatch_get_specific
传入key
来获取context
。
void dispatch_queue_set_specific(dispatch_queue_t queue, const void *key, void *context, dispatch_function_t destructor);
* queue:需要操作的队列,对这个队列添加key和context data
* key: 用于标识关联context data数据的key,此key只作为指针进行比较,不会被间接引用。因此可以使用指向特定子系统的静态变量的指针或者任意其他允许您唯一标识该值的值。不建议指定指向字符串常量的指针。空值不是键的有效值,使用空值设置上下文数据将会被忽略。
* context:与key进行关联的上下文数据,此参数可能为NULL
* destructor: 可以用来释放上下文数据的析构函数。此参数可能为空。如果context为空,则忽略析构函数
由于我们初始设置了key
, context
,如果任务块在当前的队列执行就可以通过key
获取到context
,如果不是在当前队列就获取context
为NULL
void * dispatch_get_specific(const void *key);
* key: 当前块正在其上执行的调度队列关联的key。键只作为指针进行比较,从不取消引用。不建议直接传递字符串常量。
使用dispatch_queue_get_label
判断当前队列
BOOL isMainQueue() {
static const char * MainIdentifier;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
MainIdentifier = dispatch_queue_get_label(dispatch_get_main_queue());
});
const char *identifier = dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL);
return strcmp(identifier, MainIdentifier) == 0;
}
注意:在获取当前队列的标识时,使用了DISPATCH_CURRENT_QUEUE_LABEL
,这样依然会导致dispatch_get_current_queue
可能产生的死循环问题。
dispatch_get_current_queue
导致死锁的情况分析
以下为一个使用这种方式导致死锁的场景:
- (void)testAction {
dispatch_queue_t queueA = dispatch_queue_create("com.lyk.queueA", NULL);
dispatch_queue_t queueB = dispatch_queue_create("com.lyk.queueB", NULL);
// 设置了迁移队列,同时queueA,queueB均为串行队列导致死锁
dispatch_set_target_queue(queueB, queueA);
dispatch_sync(queueB, ^{
dispatch_sync(queueA, ^{
NSLog(@"测试");
});
});
}
// 延伸,当将queueA改为并行队列就不会出现死锁了,为啥?
void dispatch_set_target_queue(dispatch_object_t object, dispatch_queue_t queue);
// 简单理解 为将object中的任务块 转到queue中去执行
GCD
队列是按照层级结构来组织的,无法单用某个队列对象来描述"当前队列"。
dispatch_get_current_queue
函数可能返回与预期不一致的结果
误用dispatch_get_current_queue
可能导致死锁
参考引申:
https://www.objc.io/issues/2-concurrency/concurrency-apis-and-pitfalls/