您的位置:首页 > 产品设计 > UI/UE

GCD的一些用法

2015-05-03 23:43 239 查看
GCD是Grand Central Dispatch 的缩写. 即多线程优化技术.

它可以提供线程安全的队列,串行队列和并行队列,同步和异步执行任务.在队列中, 有很多回调块的执行单位, 完成一个任务后就回调块继续执行.GCD负责线程的创建,管理及释放,我们不用管理者一块.

GCD队列特点:

1. 不是为了通常的数据存储而设计的

2. 它没有取消功能, 没有随机访问功能

3. 使用合理的数据结构来解决问题

4. 自动的线程创建和回收

5. 通过块来实现回调, 极大简化代码复杂度

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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  iOS GCD queue 多线程 异步