on
关于gcd
GCD是Grand Central Dispatch 的缩写. 即多线程优化技术. 它可以提供线程安全的队列,串行队列和并行队列,同步和异步执行任务.在队列中, 有很多回调块的执行单位, 完成一个任务后就回调块继续执行.GCD负责线程的创建,管理及释放,我们不用管理者一块.
GCD队列特点:
- 不是为了通常的数据存储而设计的
- 它没有取消功能, 没有随机访问功能
- 使用合理的数据结构来解决问题
- 自动的线程创建和回收
- 通过块来实现回调, 极大简化代码复杂度
GCD队列类型
- main queue 主队列
dispatch_get_main_queue()
可以取到,它是一个串行队列, 运行在主线程, 主要用做对视图的更新操作. - global queue 全局队列 ` dispatch_get_global_queue(long identifier, unsigned long flags)` 可以创建. 它是一个并行队列,
- 自定义队列. 使用
dispatch_queue_create(const char *label, dispatch_queue_attr_t attr)
创建,可以使串行或者并行, 通过 attr 参数设置
串行队列特点: 先进先出, 原子性入队(线程安全), 自动出队.每次只执行一个任务,按入队顺序执行. 并行队列特点: 它保证开始执行的顺序是入队的顺序, 但是任务之间是同时进行的, 不能保证先入队的一定先执行完成
dispatch_async()
异步方法 ,功能类似于 performSelector: onThread: withObject: waitUntilDone:
,我们何时使用呢?
- 自定义串行队列: 当我们想要串行的在后台执行一些操作,避免有资源争夺的操作同时执行, 可以使用异步方法,但是如果在操作中需要用到一些方法返回的数据, 最好在异步方法中, 同步调用返回数据.
- 主队列:通常是等待一些操作完成后更新UI
- 异步队列: 通常是一些非UI操作处理.
dispatch_sync()
同步方法 ,功能类似于 performSelector: onThread: withObject: waitUntilDone:
, 我们何时使用呢?
- 子定义串行队列,小心使用,千万不要再同一个串行队列中使用同步方法, 会造成循环死锁.
- 主队列. 小心使用, 可能会有死锁.
- 并行队列. 可以很好的通过dispatch barriers 或者 需要等待结果继续执行的任务.
简单的创建队列
dispatch_queue_t queue =dispatch_queue_create("myQueue",DISPATCH_QUEUE_SERIAL);
// DISPATCH_QUEUE_CONCURRENT 并行
// DISPATCH_QUEUE_SIRAIL 串行
dispatch_async(queue, ^{
// your implemente
});
dispatch barrier
当我们遇到线程安全读写问题的时候,比如访问或这只某些线程不安全的属性,例如 NSMutableArray, 需要使用 dispatch barrier 保证读写唯一性,它相当于一个读写锁, 会在并行队列中保证执行的唯一性,简单的理解就是一旦进入了barrier中, 不论什么队列, 执行barrier的时候, 这个队列暂时变成了串行队列.
- 自定义串行队列. 在这里使用 barrier 其实没有什么作用, 串行队列原本就是同一时间只能执行一个任务,
- 全局并行队列. 在这里不是一个好的选择, 因为会有其他的不是自己的任务被阻塞执行.
- 自定义并行队列. 在这里执行比较合适, 可以完全自定义. ``` // 这里因为可变数组是线程不安全的, 所以我们使用dispatch barrier来保证即使在多线性中访问也是安全的
- (void)addPhoto:(Photo *)photo { // Photo 是我们自定义的model // concurrentQueue 是并行队列 // _photosArray 是一个可变数组 if (photo) { dispatch_barrier_async(self.concurrentQueue,^{ [_photosArray addObject:photo]; dispatch_async(dispatch_get_main_queue(),^{ // 继续更新UI之类的 }); }); } } ```
dispatch_once
可以保证线程安全的只执行一次,非常有用,适合创建全局使用且创建昂贵的资源
+ (instancetype)sharedManager
{
static Manager *sharedManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedManager = [[Manager alloc] init];
// do other things
});
return sharedPhotoManager;
}
dispatch_after
延时执行一个任务, 相当于延迟的dispatch_async, 通常使用在main queue上,类似于performSelector: withObject: afterDelay:
的功能
int64_t delay = 1;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
// do your implement
});
在使用MRC的时候还需要对GCD队列的参数做Retain Release 操作, 在ARC就不需要了, 编译器已经自动添加了.
void performAsyncWithCallback(id object, dispatch_queue_t q) {
dispatch_retain(q); // 在MRC的时候需要添加
dispatch_async(dispatch_queue_create("q", DISPATCH_QUEUE_CONCURRENT), ^{
dispatch_async(q, ^{
[object callBack];
});
dispatch_release(q); // 在MRC的时候需要添加
});
}
Dispatch Groups
当我们向监听多个异步任务全部完成的时候,就需要为每个任务添加BOOL点判断位, 这个做非常冗余,不友好, 这时我们需要使用dispatch group 来解决, 它可以同步或异步的在完成组内的所有任务的时候发出通知. API 有两种通知方式:
- dispatch_group_wait 会阻塞当前的线程,等待租内的所有任务都完成或者超时的时候发出通知, 这是同步的.
- dispatch_group_notify 会给出一个异步的回调块,它不会阻塞所在的线程
// 异步执行任务
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
// 创建group
dispatch_group_t downloadGroup =
dispatch_group_create();
for (NSInteger i = 0; i < 3; i++) {
// 任务开始进入group
dispatch_group_enter(downloadGroup);
// 异步执行任务, 任务使group内部计数加1
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
// 执行出group, 使group 内部计数减1,达到平衡
dispatch_group_leave(downloadGroup);
});
}
// group_wait 是同步方法, 会阻塞当前前程,只有等待group中所有任务完成后 才能往下执行
dispatch_group_wait(downloadGroup, DISPATCH_TIME_FOREVER);
dispatch_async(dispatch_get_main_queue(), ^{
// some UI operation
});
// group_notify 异步方法, 异步收到group所有任务完成的通知,不会阻塞当前线程.
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// some UI operation
});
});
group也可以监听多个线程的任务,在全部结束是发出通知
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, dispatch_get_global_queue(0,0), ^{
// 并行执行的线程一
});
dispatch_group_async(group, dispatch_get_global_queue(0,0), ^{
// 并行执行的线程二
});
dispatch_group_notify(group, dispatch_get_global_queue(0,0), ^{
// 会等待两个线程全结束后, 汇总结果
});
dispatch_apply
当我们通过 for 循环添加一些任务到队列中的时候,如何才能提高添加速度呢, 我们可以使用dispatch_apply, 它可以异步执行循环内部的实现, 但是它本身是同步的方法, 需要等到循环完全结束后才返回.合适使用它才合适?
- 自定义串行队列. 不要再这里使用, 串行队列会使它无效
- 主队列. 还是一样, 串行队列使它无效
- 并行队列. 可以很好的使用它, 尤其是我们向监控任务的进度.
dispatch_apply(10, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^(size_t i) {
// 循环执行 10 次
});
dispatch_source
它可以简单轻松的监听系统事件,用户自定义事件等,它可以在任何的队列上传递并处理回调事件, 也可以执行挂起, 恢复操作, 默认创建后是挂起的.创建dispatch_source 会有很多种要监听的类型,比如:
- timer计时器的周期性通知事件
- UNIX系统的信号通知
- 文件或端口的事件:读数据, 写数据, 文件被删除,移动,修改,重命名等.
- 进程的状态事件: 进程退出, 一个信号到达进程, 进程执行或分支
- Mach
- 用户自定义的事件
创建source 函数原型, 其中 queue为source执行的,type为source类型, queue可以是我们制定的queue, 也可以是DISPATCH_TARGET_QUEUE_DEFAULT, source默认的queue, 这个队列默认是 default priority global queue,默认级别的全局队列.
dispatch_source_create(dispatch_source_type_t type,
uintptr_t handle,
unsigned long mask,
dispatch_queue_t queue);
调用示例
dispatch_source_t readSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, fd, 0, DISPATCH_TARGET_QUEUE_DEFAULT);
未完待续: dispatch_set_target_queue dispatch_queue_set_specific dispatch_data_t
具体例子可以参考我的例子 GCD例子 参考 Concurrency Programming Guide 参考1 参考2