在GCD苹果官方文档第一句话就是:
Execute code concurrently on multicore hardware by submitting work to dispatch queues managed by the system.(在多核硬件上执行分配给队列的任务)
这句话就说出了GCD的诸多优点,我们都知道GCD在iOS开发中是多线程开发中用的最多的。但是却不是像pthread和NSThread去直接操作线程的,GCD是用一个叫队列的东西来包装了线程,所以我们在开发中只需要操作好队列就可以了。完全不用去管理线程的创建和销毁。这样就大大减少了开发中遇到莫名其妙的bug。
- GCD的优势:
1.GCD会自动的管理线程的生命周期(线程的创建、任务的调度和 销毁线程)。
2.想执行什么任务就直接交给GCD不用去管理线程。
3.是为解决多核并行提出的解决方案,会自动利用CPU内核
问:我们为什么要使用多线程?
答:因为在App的运行过程中会有许多耗时的操作,比如:较大的网络图片,观看视频,听歌,书籍等资源的下载。我们需要把这些操作都放到后台的子线程上去执行,不能卡主主线程,因为我们需要提高用户体验,对App的使用者友好一点,这样我们的App才能在用户的移动设备中一直存在。
GCD核心概念:
- 任务:就是我们要执行的操作(使用block封装,block 就是一个提前准备好的代码块,需要的时候执行)
- 队列:我们要执行的操作要存放的东西(串行队列and并发队列)
GCD的操作步骤:
1.定制任务(我们要执行的操作)
2.将任务添加到队列中(GCD就会自动从队列中的任务取出任务放到对应的线程中执行,任务中的队列遵循FIFO原则:先进先出,后进后出)
GCD的对列分为2大类型(任务的执行方式):
- 并发队列:
1.可以让多个任务并发(同时)执行(自动开启多个线程同时执行任务)。
2.并发功能只有在异步函数下才有效果。在同步函数下没有效果。 - 串行队列:
1.让任务一个接一个的执行(一个任务执行完毕后才执行下一个任务)
GCD执行任务的方式(具不具备开启线程的能力):
1.同步:只在当前线程中执行任务,不会开启新的线程,因为同步不具备开启新线程的能力。会顺序执行。
2.异步:可以在新线程中执行人,会开启新的线程,因为它具备开启新线程的能力。只要有任务,就会去线程池取子线程(主线程除外)。
注意:
1.异步执行的方式并不是一定会开启线程,如果任务不是耗时操作且执行速度极快,就不会再开启新的线程,任务会继续在现有且未被收回线程池的线程上执行。
2.同步操作和队列无关。更新UI最好放在同步方法里面。
3.开不开线程取决于执行任务的函数,同步就不开,异步就开
4.开几条线程,取决于队列,串行开一条,并发开多条(异步)
GCD常见示例代码及方法详解:
- 同步执行方法:这句话不执行完成,就不会执行下一个任务。同步线程是不会开启子线程的。
-(void)GCDDemo1{
//1.创建队列 - 并发队列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
//2.任务添加到队列中
//2.1 定义了一个任务
void(^task)() = ^{
NSLog(@"%@",[NSThread currentThread]);
};
//2.2添加任务到队列
dispatch_sync(queue, task);
}
- 异步执行方法(async):如果任务没有执行完毕,可以不用等待,异步执行下一个任务。具备开启线程的能力。异步通常是多线程的代名词。
-(void)GCDDemo2{
//1.创建队列 并发队列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
//2.定义一个任务
//2.1 定义一个任务
void(^task)() = ^{
NSLog(@"%@",[NSThread currentThread]);
};
//2.2添加方法到队列
dispatch_async(queue, task);
}
- 线程间的通讯
-(void)CGDDemo3{
//指定任务的执行方法-- 异步
//开启了线程
dispatch_async(dispatch_get_global_queue(0, 0), ^{
//耗时操作
NSLog(@"%@",[NSThread currentThread]);//number=3
//更新UI
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"更新UI%@",[NSThread currentThread]);//number=1
});
});
//MARK: - 利用GCD加载图片
//异步执行
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSURL *url = [NSURL URLWithString:@"我是图片链接"];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:data];
dispatch_async(dispatch_get_main_queue(), ^{
//更新UI - 把image添加到控件上
});
});
}
- 串行队列 同步执行 (不会开线程,会顺序执行)
-(void)GCDDemo4{
//队列 -- 串行 DISPATCH_QUEUE_SERIAT 串行 == NULL
dispatch_queue_t queue = dispatch_queue_create("", NULL);
//同步
for (int i=0; i<10; i++) {
dispatch_sync(queue, ^{
NSLog(@"%@------%d",[NSThread currentThread],i);
});
}
//会依次打印i的值从0到9. 线程的number = 1
}
- 串行队列 异步执行(会开线程,但只会开一个线程,因为任务只能一个个拿,执行完一个任务,才会去取下一个任务。所以会顺序执行)
-(void)GCDDemo5{
//队列 -- 串行 DISPATCH_QUEUE_SERIAT 串行 == NULL
dispatch_queue_t queue = dispatch_queue_create("我是线程的名字String", NULL);
for (int i=0; i<10; i++) {
dispatch_async(queue, ^{
NSLog(@"%@------%d",[NSThread currentThread],i);
});
}
//打印顺序不一定,如果此操作耗时大于创建线程与执行子线程上任务的时间就会靠后执行。反之则靠前执行。在主线程上执行。
NSLog(@"come here");
}
- 并发队列 异步执行(会开线程,不会顺序执行)
-(void)GCDDemo6{
//队列 -- 并发 DISPATCH_QUEUE_CONCURRENT 并发
dispatch_queue_t queue = dispatch_queue_create("", DISPATCH_QUEUE_CONCURRENT);
for (int i=0; i<10; i++) {
dispatch_async(queue, ^{
NSLog(@"%@------%d",[NSThread currentThread],i);
});
}
//打印顺序不一定。如果此操作耗时大于创建线程与执行子线程上任务的时间就会靠后执行。反之则靠前执行。在主线程上执行。
NSLog(@"come here");
}
并发队列 同步执行(不会开线程,顺序执行)
-(void)GCDDemo7{
//队列 -- 并发
dispatch_queue_t queue = dispatch_queue_create("", DISPATCH_QUEUE_CONCURRENT);
for (int i=0; i<10; i++) {
dispatch_sync(queue, ^{
NSLog(@"%@------%d",[NSThread currentThread],i);
});
}
//最后打印
NSLog(@"come here");
}
- 同步任务加强
- 开发中,通常耗时操作放在后台执行,有的时候,任务之间有依赖关系(先后顺序)
- 利用同步任务能够做到任务依赖关系,前一个任务不执行完,队列就不会调度后面的任务
-(void)GCDDemo8{
dispatch_queue_t queue = dispatch_queue_create(0, DISPATCH_QUEUE_CONCURRENT);
//1.eg:登录 放到同步任务中,会先执行登录。必须先完成才会执行后面的.是在主线程的。如果很耗时就会有问题。demo9解决这个问题。
dispatch_sync(queue, ^{
NSLog(@"登录%@",[NSThread currentThread]);
});
//2.eg:支付
dispatch_async(queue, ^{
NSLog(@"支付%@",[NSThread currentThread]);
});
//3.eg:下载
dispatch_async(queue, ^{
NSLog(@"下载%@",[NSThread currentThread]);
});
}
- 同步任务增强
- 队列调度多个任务之前,指定一个同步任务,让所有的异步等待同步任务执行完毕后再执行,就是依赖关系。此时的同步任务就相当于一个锁。
-(void)GCDDemo9{
//异步
dispatch_queue_t queue = dispatch_queue_create(0, DISPATCH_QUEUE_CONCURRENT);
//把三个任务包装成一个任务再放到异步中
void(^task)() = ^ {
//1.eg:登录 放到同步任务中,会先执行登录。必须先完成才会执行后面的。
dispatch_sync(queue, ^{
NSLog(@"登录%@",[NSThread currentThread]);
});
//2.eg:支付
dispatch_async(queue, ^{
NSLog(@"支付%@",[NSThread currentThread]);
});
//3.eg:下载
dispatch_async(queue, ^{
NSLog(@"下载%@",[NSThread currentThread]);
});
};
dispatch_async(queue, task);
}
- 全局队列 - 本质是一个并发队列
-(void)GCDDemo10{
//全局队列
/*
参数:
- 参数1:涉及系统适配
ios 8.0 服务质量
QOS_CLASS_USER_INTERACTIVE 用户交互(希望线程快速执行,不要放耗时操作)
QOS_CLASS_USER_INITIATED 用户需要的(不要放耗时操作)
QOS_CLASS_DEFAULT 默认
QOS_CLASS_UTILITY 使用工具(耗时操作)
QOS_CLASS_BACKGROUND 后台(CPU调度次数会很少,可以忽略)
QOS_CLASS_UNSPECIFIED 没有指定优先级
ios 7.0
DISPATCH_QUEUE_PRIORITY_HIGH 2 高优先级
DISPATCH_QUEUE_PRIORITY_DEFAULT 0 默认优先级
DISPATCH_QUEUE_PRIORITY_LOW (-2) 低优先级
DISPATCH_QUEUE_PRIORITY_BACKGROUND 后台优先级(CPU调度次数会很少,可以忽略)
- 参数2:未来预留参数
提示:优先级不要选择后台优先级 。使用默认优先级可以适配7.0和8.0
*/
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
for (int i = 0; i< 10; i++) {
dispatch_async(queue, ^{
NSLog(@"%@ --- %d",[NSThread currentThread],i);
});
}
//比较靠前,创建线程需要时间所以执行靠前
NSLog(@"come here");
}
-(void)gcdDemo11{
/*
全局队列 & 并发队列
1>名称,并发队列取名字,适用于企业开发,跟踪错误
2>release 在MRC 并发队列需要使用的。全局队列不需要释放。
dispatch_release(q);//ARC 情况下不需要release
全局队列 & 串行队列
全局队列:并发,能够调度多个线程,执行效率相对高。
- 费电
串行队列:一个一个执行,执行效率相对低
- 省电
判断依据:根据用户上网方式
- wifi:可以多开线程
- 流量 :尽量少开线程
*/
//并发队列 可以取名字,适合于做企业开发跟踪错误
dispatch_queue_t q = dispatch_queue_create("全局队列和并发队列的异同", DISPATCH_QUEUE_CONCURRENT);
for (int i=0; i<10; i++) {
dispatch_async(q, ^{
NSLog(@"%@----%d",[NSThread currentThread],i);
});
}
//全局队列
dispatch_queue_t q1 = dispatch_get_global_queue(0, 0);
for (int i=0; i<10; i++) {
dispatch_async(q1, ^{
NSLog(@"%@----%d",[NSThread currentThread],i);
});
}
}
- 延时执行
#pragma mark - 延时执行
-(void)gcdDemo12{
NSLog(@"come here");
/**
DISPATCH_TIME_NOW 从现在开始
int64_t 经过多少纳秒之后
*/
dispatch_time_t when = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC));
/**
从现在开始,进过多少纳秒之后,异步执行
参数:
1。时间
2.队列
3,block
*/
dispatch_after(when, dispatch_get_main_queue(), ^{
NSLog(@"%@",[NSThread currentThread]);
});
}
- 一次执行( 单例设计模式)
// 单例设计模式中,用的非常普遍
-(void)gcdDemo13{
NSLog(@"来了");
//苹果提供的一次执行机制,不仅能够保证一次执行,而且是线程安全
static dispatch_once_t onceToken;
//推荐使用 gcd 一次执行,效率比互斥锁的效率高
dispatch_once(&onceToken, ^{
//只会执行一次
NSLog(@"执行了 %@",[NSThread currentThread]);
});
}
- 调度组(多个队列)
-(void)gcdDemo14{
//创建队列
dispatch_queue_t q = dispatch_get_global_queue(0, 0);
//创建调度组
dispatch_group_t g = dispatch_group_create();
//添加任务,让队列调度,任务执行情况,最后通知群组
dispatch_group_async(g, q, ^{
NSLog(@"下载A------%@",[NSThread currentThread]);
});
dispatch_group_async(g, q, ^{
NSLog(@"下载B------%@",[NSThread currentThread]);
});
dispatch_group_async(g, q, ^{
NSLog(@"下载C------%@",[NSThread currentThread]);
});
//执行完毕后,通知
//用一个调度组,可以监听全局队列的任务,主队列取执行最后的任务
//dispatch_group_notify这本身也是异步的(在子线程中)
dispatch_group_notify(g, q, ^{
NSLog(@"下载完成------%@",[NSThread currentThread]);
});
//在主线程中---更新UI通知用户
dispatch_group_notify(g, dispatch_get_main_queue(), ^{
NSLog(@"更新UI");
});
}
- 主队列
/**
同步任务死锁:当前是在主线程上,让主队列执行同步任务
*/
//主队列是专门负责在主线程上调度任务的队列 ---> 不会开线程
-(void)gcdDemo15{
//1.队列 ---> 一启动主线程,就可以获取主队列
dispatch_queue_t q = dispatch_get_main_queue();
//异步任务
dispatch_async(q, ^{
NSLog(@"%@",[NSThread currentThread]);
});
NSLog(@"come here");
}
- 主线程中会锁死(在主线程上)
//主线程中会锁死(在主线程上)
-(void)gcdDemo16{
//1.队列 ---> 一启动主线程,就可以获取主队列
dispatch_queue_t q = dispatch_get_main_queue();
//同步任务
//同步执行任务的特点:这一句话不执行完毕就不能执行下一句,阻塞式的。
dispatch_sync(q, ^{
NSLog(@"%@",[NSThread currentThread]);
});
NSLog(@"come here");
}
- 主队列同步任务(不锁死,就要放到异步子线程上)
//MARK: 主队列同步任务(不锁死,就要放到异步子线程上)
-(void)gcdDemo17{
void(^task)() = ^{
//1.队列 ---> 一启动主线程,就可以获取主队列
dispatch_queue_t q = dispatch_get_main_queue();
//同步任务
//同步执行任务的特点:这一句话不执行完毕就不能执行下一句,阻塞式的。
dispatch_sync(q, ^{
NSLog(@"%@",[NSThread currentThread]);
});
NSLog(@"come here");
};
//放到子线程上
dispatch_async(dispatch_get_global_queue(0, 0), task);
}
/**
dispatch_barrier_async 作用是在并行队列中,等待前面两个操作并行操作完成,这里是并行输出
dispatch-1,dispatch-2
然后执行
dispatch_barrier_async中的操作,(现在就只会执行这一个操作)执行完成后,即输出
"dispatch-barrier,
最后该并行队列恢复原有执行状态,继续并行执行
dispatch-3,dispatch-4
*/
-(void)gcdDemo18{
dispatch_queue_t concurrentQueue =dispatch_queue_create("my", NULL);
dispatch_async(concurrentQueue, ^(){
NSLog(@"dispatch-1");
});
dispatch_async(concurrentQueue, ^(){
NSLog(@"dispatch-2");
});
dispatch_barrier_async(concurrentQueue, ^(){
NSLog(@"dispatch-barrier");
});
dispatch_async(concurrentQueue, ^(){
NSLog(@"dispatch-3");
});
dispatch_async(concurrentQueue, ^(){
NSLog(@"dispatch-4");
});
}