GCD 中的队列
GCD 所提供的 API 虽然简单,但是十分强大。其提供了两种类型的队列,一种串行队列,一种并行队列。
提交到串行队列中的 block 任务遵循先进先出的顺序,由系统维护的线程池中的线程执行。虽然任务的执行线程是不确定的,但是同一个时刻,同一个队列中的任务只会有一个任务在执行。但是,不同串行队列中的任务是并行的。
提交到并行队列中的任务则不同,系统会根据需要创建线程,并行执行队列中的任务,当队列中的任务执行完毕后,这些线程便会被释放。
队列类型
类型 | 定义 | 说明 |
---|---|---|
dispatch_queue_t | DISPATCH_DECL(dispatch_queue); | 队列 |
dispatch_queue_global_t | DISPATCH_DECL_SUBCLASS(dispatch_queue_global, dispatch_queue); | 全局并行队列 |
dispatch_queue_concurrent_t | DISPATCH_DECL_SUBCLASS(dispatch_queue_concurrent, dispatch_queue); | 并行队列 |
dispatch_queue_serial_t | DISPATCH_DECL_SUBCLASS(dispatch_queue_serial, dispatch_queue); | 串行队列 |
dispatch_queue_main_t | DISPATCH_DECL_SUBCLASS(dispatch_queue_main, dispatch_queue_serial); | 主队列 |
这些定义的展开,可以参考 GCD 中的类型。
队列属性
在 GCD 中使用 dispatch_queue_attr_t
来描述队列的属性,其定义 DISPATCH_DECL(dispatch_queue_attr);
展开如下:
@protocol OS_dispatch_queue_attr <OS_dispatch_object>
@end
typedef NSObject<OS_dispatch_queue_attr> * dispatch_queue_attr_t;
常用的两个宏 DISPATCH_QUEUE_SERIAL
和 DISPATCH_QUEUE_CONCURRENT
分别表示串行队列和并行队列。
除此之外,宏 DISPATCH_QUEUE_SERIAL_INACTIVE
和 DISPATCH_QUEUE_CONCURRENT_INACTIVE
分别表示初始化的串行队列和并行队列处于不可活动状态。
实际他们都是调用了下面的函数生成的队列属性。
dispatch_queue_attr_t
dispatch_queue_attr_make_initially_inactive(
dispatch_queue_attr_t _Nullable attr);
#define DISPATCH_QUEUE_SERIAL_INACTIVE \
dispatch_queue_attr_make_initially_inactive(DISPATCH_QUEUE_SERIAL)
#define DISPATCH_QUEUE_CONCURRENT_INACTIVE \
dispatch_queue_attr_make_initially_inactive(DISPATCH_QUEUE_CONCURRENT)
应当注意的是,初始化后处于不可活动状态的队列,添加到其中的任务要想开始执行,必须先调用 dispatch_activate()
函数使其状态变更为可活动状态。
dispatch_queue_attr_t
dispatch_queue_attr_make_with_autorelease_frequency(
dispatch_queue_attr_t _Nullable attr,
dispatch_autorelease_frequency_t frequency);
该函数返回的队列属性会指明是否为任务添加单独的自动释放池,即使用 @autoreleasepool
包裹提交的 block 任务代码。
但是,这只对异步添加的任务有效,对同步添加的任务是无效的。
dispatch_autorelease_frequency_t
有如下枚举值:
DISPATCH_AUTORELEASE_FREQUENCY_INHERIT
,表示继承目标队列,是创建队列时的默认值。DISPATCH_AUTORELEASE_FREQUENCY_WORK_ITEM
,表示将异步添加的任务推入单独的自动释放池。DISPATCH_AUTORELEASE_FREQUENCY_NEVER
,表示不为添加的任务单独使用自动释放池,全局并行队列就使用了该值。
具体创建队列时,可以直接使用下面的宏,来创建需要单独使用自动释放池的队列。
#define DISPATCH_QUEUE_SERIAL_WITH_AUTORELEASE_POOL \
dispatch_queue_attr_make_with_autorelease_frequency(\
DISPATCH_QUEUE_SERIAL, DISPATCH_AUTORELEASE_FREQUENCY_WORK_ITEM)
#define DISPATCH_QUEUE_CONCURRENT_WITH_AUTORELEASE_POOL \
dispatch_queue_attr_make_with_autorelease_frequency(\
DISPATCH_QUEUE_CONCURRENT, DISPATCH_AUTORELEASE_FREQUENCY_WORK_ITEM)
dispatch_queue_attr_t
dispatch_queue_attr_make_with_qos_class(dispatch_queue_attr_t _Nullable attr,
dispatch_qos_class_t qos_class, int relative_priority);
通过 qos_class_t
类型来指定队列的属性,并设置同类型队列的相对优先级。需要注意的是 relative_priority
的取值范围是 [-15,0]
。
qos_class_t | dispatch_queue_global_t |
---|---|
QOS_CLASS_USER_INTERACTIVE | |
QOS_CLASS_USER_INITIATED | DISPATCH_QUEUE_PRIORITY_HIGH |
QOS_CLASS_DEFAULT | DISPATCH_QUEUE_PRIORITY_DEFAULT |
QOS_CLASS_UTILITY | DISPATCH_QUEUE_PRIORITY_LOW |
QOS_CLASS_BACKGROUND | DISPATCH_QUEUE_PRIORITY_BACKGROUND |
例程:
dispatch_queue_t queue;
dispatch_queue_attr_t attr;
attr = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL,
QOS_CLASS_UTILITY, 0);
queue = dispatch_queue_create("com.example.myqueue", attr);
队列获取
在使用 GCD 中函数处理任务时,需要指定任务将要提交到的队列,所以首先要获取需要的队列。
dispatch_queue_global_t
dispatch_get_global_queue(long identifier, unsigned long flags);
- identifier ,表示优先级,使用
qos_class_t
或dispatch_queue_global_t
类型均可。 - flags ,保留字段
dispatch_queue_main_t
dispatch_get_main_queue(void)
{
return DISPATCH_GLOBAL_OBJECT(dispatch_queue_main_t, _dispatch_main_q);
}
获取同主线程相关联的队列,提交到该队列中的任务都由主线程执行。
dispatch_queue_t
dispatch_queue_create(const char *_Nullable label,
dispatch_queue_attr_t _Nullable attr);
创建一个队列,队列的引用计数会在添加任务时增一,任务结束后减一。并且,传递的属性 attr
如果是 qos_class_t
类型的,那么其优先于新建队列的目标队列的优先级属性,只要该参数值不会降低其继承的优先级。
- label ,队列的名称,可以为空。
- attr ,队列的属性,可选值如下:
DISPATCH_QUEUE_SERIAL
DISPATCH_QUEUE_SERIAL_INACTIVE
DISPATCH_QUEUE_CONCURRENT
DISPATCH_QUEUE_CONCURRENT_INACTIVE
dispatch_queue_attr_make_with_*
函数得到的dispatch_queue_attr_t
类型的其他值
dispatch_queue_t
dispatch_queue_create_with_target(const char *_Nullable label,
dispatch_queue_attr_t _Nullable attr, dispatch_queue_t _Nullable target)
DISPATCH_ALIAS_V2(dispatch_queue_create_with_target);
该函数除了指定队列的名称和属性,还可以指定队列的目标队列。并且,参数 attr
如果使用的是 qos_class_t
类型,而参数 target
使用的是全局并行队列,那么该全局队列将使用参数 attr
所指定的优先级。
参数 target
传递 DISPATCH_TARGET_QUEUE_DEFAULT
则会指定默认队列为所创建队列的目标队列。
除了在创建时指定目标队列外,还可以修改处于非活动状态队列的目标队列。
void dispatch_set_target_queue(dispatch_object_t object,
dispatch_queue_t _Nullable queue);
各个单独的串行队列是可以并行的,但是如果他们拥有一个相同的串行目标队列,那么这些串行队列中的任务则只能一个一个执行。
如下例程,最终的输出结果总是一致的。
dispatch_queue_t serialQueue = dispatch_queue_create("serialQueue", DISPATCH_QUEUE_SERIAL);
NSMutableArray *queues = [NSMutableArray array];
for (int i = 0; i < 10; i++) {
NSString *label = [NSString stringWithFormat:@"queue%i",i];
dispatch_queue_t queue = dispatch_queue_create(label.UTF8String, DISPATCH_QUEUE_SERIAL);
[queues addObject:queue];
dispatch_set_target_queue(queue, serialQueue);
}
for (dispatch_queue_t queue in queues) {
dispatch_async(queue, ^{
const char *label = dispatch_queue_get_label(queue);
NSLog(@"%s",label);
});
}
队列特性
获取指定队列的名称,如果传递 DISPATCH_CURRENT_QUEUE_LABEL
参数,则获取当前队列的名称。
const char * dispatch_queue_get_label(dispatch_queue_t _Nullable queue);
获取创建队列时指定的优先级,并且返回值为 qos_class_t
类型。如果创建时,并没有指定相对优先级,那么传递的参数 relative_priority_ptr
将被置为 0 。
dispatch_qos_class_t
dispatch_queue_get_qos_class(dispatch_queue_t queue,
int *_Nullable relative_priority_ptr);
例程如下:
int a ;
dispatch_queue_attr_t attr = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL,
QOS_CLASS_USER_INTERACTIVE, -5);
dispatch_queue_t queue = dispatch_queue_create("test", attr);
dispatch_qos_class_t qos = dispatch_queue_get_qos_class(queue, &a);
const char *queueLabel = dispatch_queue_get_label(queue);
NSLog(@"The queue named '%s' has quality of service : 0x%X",queueLabel,qos);
最终输出:
The queue named 'test' has quality of service : 0x21
void dispatch_queue_set_specific(dispatch_queue_t queue, const void *key,
void *_Nullable context, dispatch_function_t _Nullable destructor);
void *_Nullable dispatch_queue_get_specific(dispatch_queue_t queue, const void *key);
这两个函数可以用来设置/获取指定队列的上下文,在重置指定队列的某个指定 key 的上下文时,最初指定的 destructor
函数便会执行。
另外,该 destructor
函数还会在队列被释放时执行,如下例程:
void func(void *context) {
NSLog(@"func : %s",(char *)context);
}
void func1 (void *context) {
NSLog(@"func1 : %s",(char *)context);
}
void func2 (void *context) {
NSLog(@"func2 : %s",(char *)context);
}
- (void)test {
dispatch_queue_t serialQueue = dispatch_queue_create("serialQueue", DISPATCH_QUEUE_SERIAL);
const void *key = &key;
char *context = "context";
dispatch_queue_set_specific(serialQueue, key, (void *)context, &func);
char *context1 = "context1";
dispatch_queue_set_specific(serialQueue, key, context1, &func1);
const void *key2 = &key2;
char *context2 = "context2";
dispatch_queue_set_specific(serialQueue, key2, context2, &func2);
}
每次执行,指定的三个函数都会执行,并且输出的结果顺序并不是固定的,即他们是并行的。
void *_Nullable dispatch_get_specific(const void *key);
获取当前队列中指定 key 的上下文,如果当前队列并未指定,那么返回其目标队列中的上下文,如果没有,返回 NULL 。
void dispatch_main(void);
执行该函数,用来触发主队列中的任务执行,该方法不会返回。实际上,应用启动后,会自动执行 NSApplicationMain()
或 CFRunLoopRun()
函数,并不需要在主动调用该函数。
常用函数
-
同步/异步执行任务
void dispatch_async(dispatch_queue_t queue, dispatch_block_t block); void dispatch_async_f(dispatch_queue_t queue, void *_Nullable context, dispatch_function_t work); void dispatch_sync(dispatch_queue_t queue, DISPATCH_NOESCAPE dispatch_block_t block); void dispatch_sync_f(dispatch_queue_t queue, void *_Nullable context, dispatch_function_t work);
-
重复并行执行
void dispatch_apply(size_t iterations, dispatch_queue_t DISPATCH_APPLY_QUEUE_ARG_NULLABILITY queue, DISPATCH_NOESCAPE void (^block)(size_t)); void dispatch_apply_f(size_t iterations, dispatch_queue_t DISPATCH_APPLY_QUEUE_ARG_NULLABILITY queue, void *_Nullable context, void (*work)(void *_Nullable, size_t));
- iterations ,指定 block 任务或 函数 work 执行的次数。
- queue ,任务执行队列,为并行队列时,任务才会并行执行。
- context ,上下文传递。
- size_t ,任务执行的次数索引。
-
延迟执行
void dispatch_after(dispatch_time_t when, dispatch_queue_t queue, dispatch_block_t block); void dispatch_after_f(dispatch_time_t when, dispatch_queue_t queue, void *_Nullable context, dispatch_function_t work);
-
添加栅栏任务
void dispatch_barrier_async(dispatch_queue_t queue, dispatch_block_t block);
void dispatch_barrier_async_f(dispatch_queue_t queue,
void *_Nullable context, dispatch_function_t work);
void dispatch_barrier_sync(dispatch_queue_t queue,
DISPATCH_NOESCAPE dispatch_block_t block);
void dispatch_barrier_sync_f(dispatch_queue_t queue,
void *_Nullable context, dispatch_function_t work);
同步或异步添加任务到指定的队列中,并且该任务为栅栏任务,在其之前添加的任务未执行完毕前,该任务不会执行,并且该任务执行完毕后,在其之后添加的任务才会执行。
断言函数
-
验证当前 block 任务是在指定的队列中执行的。
void dispatch_assert_queue(dispatch_queue_t queue) DISPATCH_ALIAS_V2(dispatch_assert_queue);
-
验证当前 block 任务是在指定队列中执行的,并且是栅栏任务。
void dispatch_assert_queue_barrier(dispatch_queue_t queue);
-
验证当前 block 任务不是在指定队列中执行的。
void dispatch_assert_queue_not(dispatch_queue_t queue) DISPATCH_ALIAS_V2(dispatch_assert_queue_not);
具体使用时,应使用相应的宏定义。
#define dispatch_assert_queue_debug(q) dispatch_assert_queue(q)
#define dispatch_assert_queue_barrier_debug(q) dispatch_assert_queue_barrier(q)
#define dispatch_assert_queue_not_debug(q) dispatch_assert_queue_not(q)