GCD(Grand Central Dispatch) 介绍
GCD属于系统级的线程管理,在Dispatch queue中执行需要执行的任务性能非常的高。GCD中的FIFO队列称为dispatch queue,用来保证先进来的任务先得到执行。
1、基本概念
1、执行任务的方式
①同步 sync
同步添加任务到指定的队列中,在添加的任务执行结束之前,会一直等待,直到队列里面的任务完成之后再继续执行。
只能在当前线程中执行任务,不具备开启新线程的能力
②异步 async
异步添加任务到指定的队列中,它不会做任何等待,可以继续执行任务。
可以在新的线程中执行任务,具备开启新线程的能力。
2、队列
系统级:
①dispatch_get_global_queue 全局队列,一个并行的队列
②dispatch_get_main_queue 主队列,主线程中的唯一队列,一个串行队列
自定义:
dispatch_queue_create(const char *_Nullable label,
dispatch_queue_attr_t _Nullable attr)
2、介绍
1、dispatch_get_global_queue(long identifier, unsigned long flags)
全局并发队列,系统级:dispatch_get_global_queue
参数:
identifier:优先级,只是CPU调用的级别不同,不保证前后顺序
#define DISPATCH_QUEUE_PRIORITY_HIGH 2
#define DISPATCH_QUEUE_PRIORITY_DEFAULT 0
#define DISPATCH_QUEUE_PRIORITY_LOW (-2)
#define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN
flags:保留值。填0
注:
DISPATCH_QUEUE_PRIORITY_HIGH映射到QOS_CLASS_USER_INITIATED类。
DISPATCH_QUEUE_PRIORITY_DEFAULT映射到QOS_CLASS_DEFAULT类。
DISPATCH_QUEUE_PRIORITY_LOW映射到QOS_CLASS_UTILITY类。
DISPATCH_QUEUE_PRIORITY_BACKGROUND映射到QOS_CLASS_BACKGROUND类。
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
2、dispatch_queue_create(const char *_Nullable label,dispatch_queue_attr_t _Nullable attr)
参数:
label:const char * 类型 标签,可选,可为NULL
attr:
串行 DISPATCH_QUEUE_SERIAL或NULL(开辟一个子线程,任务在这个线程里顺序执行)
并发 DISPATCH_QUEUE_CONCURRENT(开辟多个子线程,任务在各个线程里并发执行)
dispatch_queue_t queue = dispatch_queue_create("xixi", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"%@",[NSThread currentThread]);
});
也可以给自定义队列设置优先级,有两种方式:dispatch_queue_attr_make_with_qos_class、dispatch_set_target_queue
dispatch_queue_attr_make_with_qos_class
attr:同上
qos_class:
QOS_CLASS_USER_INTERACTIVE:user interactive等级表示任务需要被立即执行提供好的体验,用来更新UI,响应事件等。这个等级最好保持小规模。
QOS_CLASS_USER_INITIATED:user initiated等级表示任务由UI发起异步执行。适用场景是需要及时结果同时又可以继续交互的时候。
QOS_CLASS_UTILITY:utility等级表示需要长时间运行的任务,伴有用户可见进度指示器。经常会用来做计算,I/O,网络,持续的数据填充等任务。这个任务节能。
QOS_CLASS_BACKGROUND:background等级表示用户不会察觉的任务,使用它来处理预加载,或者不需要用户交互和对时间不敏感的任务。
relative_priority:负偏移量 该值必须小于0且大于或等于QOS_MIN_RELATIVE_PRIORITY,否则此函数返回NULL(QOS_MIN_RELATIVE_PRIORITY是-15)
dispatch_queue_attr_t attr = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_CONCURRENT, QOS_CLASS_DEFAULT, -1);
dispatch_queue_t queue = dispatch_queue_create("xixi",attr);
dispatch_async(queue, ^{
NSLog(@"%@",[NSThread currentThread]);
});
dispatch_set_target_queue
dispatch_queue_t queue = dispatch_queue_create("xixi",NULL); //需要设置优先级的queue
dispatch_queue_t tQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0); //参考优先级
dispatch_set_target_queue(queue, tQueue); //设置queue和tQueue的优先级一样
dispatch_async(queue, ^{
NSLog(@"%@",[NSThread currentThread]);
});
dispatch_set_target_queue:可以设置优先级,也可以设置队列层级体系,比如让多个串行和并行队列在统一一个串行队列里串行执行,如下
dispatch_queue_t serialQueue = dispatch_queue_create("serialqueue", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t firstQueue = dispatch_queue_create("firstqueue", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t secondQueue = dispatch_queue_create("secondqueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_set_target_queue(firstQueue, serialQueue);
dispatch_set_target_queue(secondQueue, serialQueue);
dispatch_async(firstQueue, ^{
NSLog(@"1");
[NSThread sleepForTimeInterval:3.f];
});
dispatch_async(secondQueue, ^{
NSLog(@"2");
[NSThread sleepForTimeInterval:2.f];
});
dispatch_async(secondQueue, ^{
NSLog(@"3");
[NSThread sleepForTimeInterval:1.f];
});
3、dispatch_after
延时提交block,不会阻塞线程
参数:
dispatch_time_t when:dispatch_time(dispatch_time_t when, int64_t delta)
when有两个值:
DISPATCH_TIME_NOW 当前
DISPATCH_TIME_FOREVER 遥远的未来
delta:纳秒
#define NSEC_PER_SEC 1000000000ull //每秒有多少纳秒
#define USEC_PER_SEC 1000000ull //每秒有多少毫秒
#define NSEC_PER_USEC 1000ull //每毫秒有多少纳秒
1秒可以这样写
dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC);
dispatch_time(DISPATCH_TIME_NOW, 1000 * USEC_PER_SEC);
dispatch_time(DISPATCH_TIME_NOW, USEC_PER_SEC * NSEC_PER_USEC);
dispatch_queue_t queue:队列
dispatch_block_t block:block
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
//
});
4、dispatch_barrier_async
栅栏函数,用于提交的闭包是指定队列中在特定时段唯一在执行的一个。
注:只在自己创建的队列上有这种作用,在全局并发队列和串行队列上,效果和dispatch_sync一样
dispatch_queue_t dataQueue = dispatch_queue_create("dataqueue", DISPATCH_QUEUE_CONCURRENT);
// 先执行这两步
dispatch_async(dataQueue, ^{
[NSThread sleepForTimeInterval:2.f];
NSLog(@"read data 1 %@",[NSThread currentThread]);
});
dispatch_async(dataQueue, ^{
NSLog(@"read data 2 %@",[NSThread currentThread]);
});
// 前面执行完才执行这步
dispatch_barrier_async(dataQueue, ^{
NSLog(@"write data 1 %@",[NSThread currentThread]);
[NSThread sleepForTimeInterval:1];
});
//再执行这步
dispatch_barrier_async(dataQueue, ^{
NSLog(@"write data 2 %@",[NSThread currentThread]);
[NSThread sleepForTimeInterval:1];
});
// 最后再执行这两步
dispatch_async(dataQueue, ^{
[NSThread sleepForTimeInterval:1.f];
NSLog(@"read data 3 %@",[NSThread currentThread]);
});
dispatch_async(dataQueue, ^{
NSLog(@"read data 4 %@",[NSThread currentThread]);
});
5、dispatch_apply
快速迭代
dispatch_get_main_queue会死锁
阻塞当前线程
NSLog(@"开始%@",[NSThread currentThread]);
dispatch_apply(10,dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(size_t i) {
NSLog(@"%zu %@",i,[NSThread currentThread]);
});
NSLog(@"结束%@",[NSThread currentThread]);
6、dispatch_group_t
dispatch groups是专门用来监视多个异步任务。dispatch_group_t实例用来追踪不同队列中的不同任务。
当group里所有事件都完成GCD API有两种方式发送通知,第一种是dispatch_group_wait,会阻塞当前进程,等所有任务都完成或等待超时。第二种方法是使用dispatch_group_notify,异步执行闭包,不会阻塞。
dispatch_queue_t conQueue = dispatch_queue_create("xx", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, conQueue, ^{
NSLog(@"1 %@",[NSThread currentThread]);
});
dispatch_group_async(group, conQueue, ^{
NSLog(@"2 %@",[NSThread currentThread]);
});
//阻塞
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
// dispatch_group_notify(group, conQueue, ^{
// NSLog(@"end %@",[NSThread currentThread]);
// });
NSLog(@"go on %@",[NSThread currentThread]);
注:
dispatch_group_async等价于dispatch_group_enter() 和 dispatch_group_leave()的组合。
dispatch_group_enter() 必须运行在 dispatch_group_leave() 之前。
dispatch_group_enter() 和 dispatch_group_leave() 需要成对出现的
7、dispatch_semaphore_t
信号量:可用来控制访问资源的数量的标识,设定了一个信号量,在线程访问之前,加上信号量的处理,可告知系统按照我们指定的信号量数量来执行多个线程。
其实,这有点类似锁机制了,只不过信号量都是系统帮助我们处理了,我们只需要在执行线程之前,设定一个信号量值,并且在使用时,加上信号量处理方法就行了。
//创建信号量,参数:信号量的初值,如果小于0则会返回NULL
dispatch_semaphore_create(信号量值)
//等待降低信号量
dispatch_semaphore_wait(信号量,等待时间) -1
//提高信号量
dispatch_semaphore_signal(信号量) +1
dispatch_semaphore_t semaphore = dispatch_semaphore_create(2);
dispatch_queue_t quene = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//任务1
dispatch_async(quene, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"run task1");
sleep(1);
NSLog(@"end task1");
dispatch_semaphore_signal(semaphore);
});
//任务2
dispatch_async(quene, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"run task2");
sleep(1);
NSLog(@"end task2");
dispatch_semaphore_signal(semaphore);
});
//任务3
dispatch_async(quene, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"run task3");
sleep(1);
NSLog(@"end task3");
dispatch_semaphore_signal(semaphore);
});
控制台信息
2018-04-17 14:44:31.909536+0800 test[3543:656486] run task1
2018-04-17 14:44:31.909536+0800 test[3543:656487] run task2
2018-04-17 14:44:32.912763+0800 test[3543:656486] end task1
2018-04-17 14:44:32.912784+0800 test[3543:656487] end task2
2018-04-17 14:44:32.912999+0800 test[3543:656488] run task3
2018-04-17 14:44:33.916648+0800 test[3543:656488] end task3
可以看出,我们设置的信号量为2,任务最多执行两个。当某个任务完成采取执行任务3
参考文章:细说GCD(Grand Central Dispatch)如何用