GCD
GCD全称Grand Central Dispatch; 是由C语言编写而成。
优点
- GCD 是系统级别运行, 根据应用所需来分配系统资源, 弄够有效的利用多核,是系统或应用所以更快,更高效的运行。
- 在ARC下 GCD和正常的Object一样,生命周期由系统进行管理。
- 使用简单按照业务需求,给GCD添加任务即可。
缺点
- 但是创建过多的线程,会耗费更多的系统资源(主线程1M, 其他线程512KB), 降低系统的性能
- 线程越多会增加调度的开销
最直观的表现是 手机发热,耗电快; 所以: 线程虽好,可不要多创建。
基本使用
同步/异步任务
任务: 就是执行什么操作(做什么)
同步任务: 不会开启新线程, 所以在一个队列中任务按顺序一个一个执行。
异步任务:会根据需要开启新线程, 队列中的任务可以在多个线程执行,相当于同时执行。
串行/并发队列
队列: 存放任务的线性表 先进先出(FIFO)
串行队列: 在此队列上的任务只能使用一个线程。
并行队列:在此队列上的任务根据需要可以使用多个线程。
具体执行顺序是和任务+队列
任务和队列的组合
系统中包含的队列
主队列
dispatch_get_main_queue();
主队列是串行队列
放入主队列的任务都是是主线程中执行,主线程也称作UI线程,主要做一些用户操作,比如刷新UI, 用户操作事件(点击,滑动等); 因为是串行队列不能向主队列中放入耗时操作,这些会手机卡顿。
全局队列
dispatch_queue_global_t dispatch_get_global_queue(long identifier, unsigned long flags);
全局队列是异步队列
参数 identifier
iOS8之后 服务质量 QOS(quality of service)
QOS_CLASS_USER_INTERACTIVE UI和动画
QOS_CLASS_USER_INITIATED 用户操作点击 拖拽等
QOS_CLASS_DEFAULT 默认
QOS_CLASS_UTILITY 耗时操作 如下载等
QOS_CLASS_BACKGROUND 后台操作
与之对应的
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
参数 flags
预留参数 传0以外的数可能return NULL
自定义队列
dispatch_queue_t
dispatch_queue_create(const char *_Nullable label,
dispatch_queue_attr_t _Nullable attr);
const char *_Nullable label
队列标签 队列名 可以在调用栈中看到任务所执行的队列
dispatch_queue_attr_t _Nullable attr
dispatch_queue_attr_t attr = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_DEFAULT, 0);
第一个参数 : 表示队列的类型 串行/并发
第二个参数: 服务质量
对三个 : 相对优先级 范围[-15, 0] 不在范围内返回NULL
使用
//创建一个串行队列 qos:default
dispatch_queue_attr_t attr = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_DEFAULT, 0);
dispatch_queue_t queue = dispatch_queue_create("queue", attr);
//还可以这样创建
//创建一个串行队列 qos:默认 默认default 下面两种一样
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t queue = dispatch_queue_create("queue", NULL);
DISPATCH_QUEUE_SERIAL 串行。(DISPATCH_QUEUE_SERIAL == NULL)
DISPATCH_QUEUE_CONCURRENT 并发
任务和队列的组合
同步任务 + 串行队列
//同步 + 串行
dispatch_queue_t queue = dispatch_queue_create("Queue", NULL);
NSLog(@"start");
dispatch_sync(queue, ^{
NSLog(@"1");
});
dispatch_sync(queue, ^{
NSLog(@"2");
});
dispatch_sync(queue, ^{
NSLog(@"3");
});
NSLog(@"end");
想串行队列中添加同步任务
所以顺序执行 输出顺序 start -> 1 -> 2 -> 3 ->end
- 同步任务不开启新线程 所以使用当前线程即主线程
- 同步队列只使用一个线程
同步任务 + 并发队列
//同步 + 并发
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"start");
dispatch_sync(queue, ^{
NSLog(@"1");
});
dispatch_sync(queue, ^{
NSLog(@"2");
});
dispatch_sync(queue, ^{
NSLog(@"3");
});
NSLog(@"end");
所以顺序执行 输出顺序 start -> 1 -> 2 -> 3 ->end
- 同步任务不开启新线程 所以使用当前线程主线程
- 并发队列可以使用多个线程,但是当前只有一个线程可以使用
异步任务 + 串行队列
//异步 + 串行
dispatch_queue_t queue = dispatch_queue_create("queue1", NULL);
NSLog(@"start");
dispatch_async(queue, ^{
NSLog(@"1");
});
dispatch_async(queue, ^{
NSLog(@"2");
});
dispatch_async(queue, ^{
NSLog(@"3");
});
NSLog(@"end");
只使用一个线程 所以只开启一个线程 在队列中的任务顺序执行
start 和end在主线程中顺序执行 1 -> 2 -> 3 新线程中顺序执行 具体顺序不定
- 异步任务可以开启多个线程
- 串行队列只使用一个线程
异步任务 + 并发队列
//异步 + 并发
dispatch_queue_t queue = dispatch_queue_create("queue1", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"start");
dispatch_async(queue, ^{
NSLog(@"1");
});
dispatch_async(queue, ^{
NSLog(@"2");
});
dispatch_async(queue, ^{
NSLog(@"3");
});
NSLog(@"end");
start 和end在主线程中顺序执行。1 ,2,3 在新线程中执行 具体开启几个线程由系统决定 执行顺序不确定
主队列和全局队列
主队列: 串行队列 使用主线程 任务 用户操作,比如刷新UI, 用户操作事件(点击,滑动等);
全局队列:并发队列
- 异步任务可以开启多个线程
- 并发队列可以使用多个线程
向主队列中添加同步任务,会造成死锁; 因为同步任务+串行队列 任务完成后才能进行下一个,主队列的任务会和添加到主队列的同步任务相互等待对方完成任务 互不相让导致死锁。
全局队列:不存在死锁情况。
从GCD的角度上说 死锁发生的情况是: 在任务中向该任务所在的任务中添加另一个同步任务
其他使用
dispatch_apply() 并行操作
dispatch_apply(99, dispatch_get_global_queue(0, 0), ^(size_t i) {
NSLog(@"%zu", i);
});
NSLog(@"end");
输出:0-98不确定顺序 最后end 阻塞当前线程
并发是逻辑上的同时发生,并行更多是侧重于物理上的同时发生
dispatch_queue_create_with_target
在target队列中创建队列
dispatch_queue_t queue1 = dispatch_queue_create_with_target(nil, DISPATCH_QUEUE_CONCURRENT, dispatch_get_main_queue());
dispatch_queue_t queue2 = dispatch_queue_create_with_target(nil, DISPATCH_QUEUE_CONCURRENT, dispatch_get_main_queue());
dispatch_queue_t queue3 = dispatch_queue_create_with_target(nil, DISPATCH_QUEUE_CONCURRENT, dispatch_get_main_queue());
dispatch_async(queue1, ^{
NSLog(@"1 start");
[NSThread sleepForTimeInterval:1.f];
NSLog(@"1 end");
});
dispatch_async(queue2, ^{
NSLog(@"2 start");
[NSThread sleepForTimeInterval:1.f];
NSLog(@"2 end");
});
dispatch_async(queue3, ^{
NSLog(@"3 start");
[NSThread sleepForTimeInterval:1.f];
NSLog(@"3 end");
});
结果顺序为 1 start -> 1 end -> 2 start -> 2 end -> 3 start -> 3end
queue1,queue2,queue3 是并发队列 但是dispatch_queue_create_with_target 相当于把 queue1,queue2,queue3放到一个串行队列中执行。
dispatch_set_target_queue
//把第二个参数 堪称targetQueue
void
dispatch_set_target_queue(dispatch_object_t object,
dispatch_queue_t _Nullable queue);
dispatch_set_target_queue 和上面的dispatch_queue_create_with_target类似
dispatch_set_target_queue 相当于把第一个队列放到第二个队列中执行 相应的队列的优先级向targetQueue看齐
dispatch_queue_t targetQueue = dispatch_queue_create("targetQueue", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t queue1 = dispatch_queue_create(nil, DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_t queue2 = dispatch_queue_create(nil, DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_t queue3 = dispatch_queue_create(nil, DISPATCH_QUEUE_CONCURRENT);
dispatch_set_target_queue(queue1, targetQueue);
dispatch_set_target_queue(queue2, targetQueue);
dispatch_set_target_queue(queue3, targetQueue);
dispatch_async(queue1, ^{
NSLog(@"1 start");
[NSThread sleepForTimeInterval:1.f];
NSLog(@"1 end");
});
dispatch_async(queue2, ^{
NSLog(@"2 start");
[NSThread sleepForTimeInterval:1.f];
NSLog(@"2 end");
});
dispatch_async(queue3, ^{
NSLog(@"3 start");
[NSThread sleepForTimeInterval:1.f];
NSLog(@"3 end");
});
结果顺序为 1 start -> 1 end -> 2 start -> 2 end -> 3 start -> 3end
dispatch_after
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"do");
});
NSLog(@"end");
延后执行 end -> do
dispatch_barrier_async 异步屏障
dispatch_queue_t queue = dispatch_queue_create(nil, DISPATCH_QUEUE_CONCURRENT);
NSLog(@"start");
dispatch_async(queue, ^{
NSLog(@"1");
});
dispatch_async(queue, ^{
NSLog(@"2");
});
dispatch_barrier_async(queue, ^{
NSLog(@"3-start");
[NSThread sleepForTimeInterval:1.f];
NSLog(@"3-end");
});
dispatch_async(queue, ^{
NSLog(@"4");
});
dispatch_async(queue, ^{
NSLog(@"5");
});
NSLog(@"end");
log结果 start end在start之后位置。 1 和 2 不确定。3-start -> 3-end 4和5 不确定
dispatch_barrier_async 在同一个队列中 dispatch_barrier_async之前的任务完成后才会执行dispatch_barrier_async中的任务, dispatch_barrier_async完成后才会继续剩下的任务
dispatch_barrier_async 不阻塞当前线程
dispatch_barrier_sync 同步屏障
dispatch_queue_t queue = dispatch_queue_create(nil, DISPATCH_QUEUE_CONCURRENT);
NSLog(@"start");
dispatch_async(queue, ^{
NSLog(@"1");
});
dispatch_async(queue, ^{
NSLog(@"2");
});
dispatch_barrier_sync(queue, ^{
NSLog(@"3-start");
[NSThread sleepForTimeInterval:1.f];
NSLog(@"3-end");
});
dispatch_async(queue, ^{
NSLog(@"4");
});
dispatch_async(queue, ^{
NSLog(@"5");
});
NSLog(@"end");
log结果 start ->. 1,2 不定 -> 3-start -> 3-end -> 4,5,end 不定
和dispatch_barrier_async 共同点就是 barrier 之前任务完成 进行barrier中的任务 barrier完成后 继续下面的任务
dispatch_barrier_sync 阻塞当前线程 所以end在 dispatch_barrier_sync任务之后
dispatch_queue_set_specific , dispatch_queue_get_specific和dispatch_get_specific
const void * sepcific = "specific";
void *context = @"context";
dispatch_queue_t queue = dispatch_queue_create(nil, DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_set_specific(queue, sepcific, context, NULL);
void *result = dispatch_queue_get_specific(queue, sepcific);
NSLog(@"%@", result); // log context
NSLog(@"%@", dispatch_get_specific(sepcific));// log null
dispatch_async(queue, ^{
NSLog(@"%@", dispatch_get_specific(sepcific)); // log context
});
dispatch_queue_set_specific 给队列绑定一个标示 key -> context
dispatch_queue_get_specific 根据key 去除context
dispatch_get_specific 根据key 取出当前block所在队列的 context
dispatch_assert_queue dispatch_assert_queue_barrier dispatch_assert_queue_not
验证current block 是否在是不是在执行在给定的队列上
dispatch_queue_t queue = dispatch_queue_create(nil, DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
//当前block 在 queue中
dispatch_assert_queue(queue);
});
dispatch_barrier_sync(queue, ^{
//当前 dispatch_barrier_sync 的block 在queue中
dispatch_assert_queue_barrier(queue);
});
//主线程的block 不在queue中
dispatch_assert_queue_not(queue);
dispatch_group
调度组
// 创建队列
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_queue_create(nil, DISPATCH_QUEUE_CONCURRENT);
// 添加异步任务
dispatch_group_async(group, queue, ^{
NSLog(@"1");
[NSThread sleepForTimeInterval:1];
});
// 添加异步任务
dispatch_group_async(group, queue, ^{
NSLog(@"2");
[NSThread sleepForTimeInterval:1];
});
// 等待 group中任务完成 才会向下执行。阻塞当前线程
long l = dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
NSLog(@"end %ld",l);
//group中任务完成后 会执行block
dispatch_group_notify(group, queue, ^{
NSLog(@"group end");
});
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_queue_create(nil, DISPATCH_QUEUE_CONCURRENT);
//手动指出 block 添加到 group中
dispatch_group_enter(group);
dispatch_async(queue, ^{
NSLog(@"1");
[NSThread sleepForTimeInterval:1];
//手动指出 block 中结束
dispatch_group_leave(group);
});
//手动指出 block 添加到 group中
dispatch_group_enter(group);
dispatch_async(queue, ^{
NSLog(@"1");
[NSThread sleepForTimeInterval:1];
//手动指出 block 中结束
});
dispatch_group_notify(group, queue, ^{
NSLog(@"group end");
});
dispatch_semaphore
信号量 线程协调完成特定的事件 管理线程资源
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
dispatch_queue_t queue = dispatch_queue_create(nil, DISPATCH_QUEUE_CONCURRENT);
//减少信号量 -1 现在信号量为0 在返回之前如果信号量小于0 dispatch_semaphore_wait会一直等待或直到超时
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
dispatch_async(queue, ^{
NSLog(@"1 - start");
[NSThread sleepForTimeInterval:1];
NSLog(@"1 - end");
//信号量+1 如果之前信号量小于0 在返回之前则唤起线程
dispatch_semaphore_signal(semaphore);
});
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
dispatch_async(queue, ^{
NSLog(@"2 - start");
[NSThread sleepForTimeInterval:1];
NSLog(@"2 - end");
dispatch_semaphore_signal(semaphore);
});
log 顺序 1 - start -> 1 - end -> 2 - start -> 2 - end
dispatch_source
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC, 0 * NSEC_PER_SEC);
dispatch_source_set_event_handler(timer, ^{
NSLog(@"do");
});
dispatch_resume(timer);