GCD 中的队列

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u011374318/article/details/88627502

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_SERIALDISPATCH_QUEUE_CONCURRENT 分别表示串行队列和并行队列。
除此之外,宏 DISPATCH_QUEUE_SERIAL_INACTIVEDISPATCH_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_tdispatch_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() 函数,并不需要在主动调用该函数。

常用函数

  1. 同步/异步执行任务

    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);
    

    参考多线程

  2. 重复并行执行

    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 ,任务执行的次数索引。
  3. 延迟执行

    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);
    
  4. 添加栅栏任务

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);

同步或异步添加任务到指定的队列中,并且该任务为栅栏任务,在其之前添加的任务未执行完毕前,该任务不会执行,并且该任务执行完毕后,在其之后添加的任务才会执行。

断言函数

  1. 验证当前 block 任务是在指定的队列中执行的。

    void dispatch_assert_queue(dispatch_queue_t queue)
    		DISPATCH_ALIAS_V2(dispatch_assert_queue);
    
  2. 验证当前 block 任务是在指定队列中执行的,并且是栅栏任务。

    void dispatch_assert_queue_barrier(dispatch_queue_t queue);
    
  3. 验证当前 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)

猜你喜欢

转载自blog.csdn.net/u011374318/article/details/88627502
gcd