程序示例Demo
1,同步异步,并行串行
各种队列执行效果
有4个术语比较容易混淆:同步、异步、并发、串行
同步和异步决定了要不要开启新的线程
同步:在当前线程中执行任务,不具备开启新线程的能力
异步:在新的线程中执行任务,具备开启新线程的能力
并发和串行决定了任务的执行方式
并发:多个任务并发(同时)执行
串行:一个任务执行完毕后,再执行下一个任务
1.0 核心概念
/* 核心概念:
任务:block中的内容
队列:把任务放到队列里面,队列有先进先出的原则
串行队列:顺序,一个一个的执行(必须前一个任务执行完成之后,才能从队列中拿出来下一个任务)
并行队列:同时,可以同时执行多个任务(可以同时从队列中取出多个任务,只要有线程去执行)
同步sync:不会开辟新线程, 只要是同步执行,不管是什么类型的队列,都会一个一个的顺序执行,同步任务会立刻马上被执行。
异步async:一定会开辟新线程,多线程的代名词
串行队列同步执行:不开新线程,在原有队列中一个一个顺序执行
串行队列异步执行:开辟一个新线程,队列中的任务在新开辟的线程中一个一个顺序执行
并行队列异步执行:开辟多个线程,并发执行,队列中任务被执行完成的顺序无法确定
并行队列同步执行:不开新线程,在原有队列中一个一个顺序执行
总结:
1,开不开线程由执行任务的方法决定,同步一定不开辟新线程,异步一定会开启新线程
2,开多少个线程由队列决定,串行 最多开一个线程,并行,可以开启多个线程,具体开多少个线程由GCD底层决定,程序员不能控制。
**/
1.1 串行队列,同步任务
/*
串行队列:队列中的任务,顺序,一个一个的执行
同步任务:不开辟新线程,一个一个的执行
结果:没有开辟新线程,在当前线程中顺序执行
dispatch:调度,GCD里面的函数,都是以dispatch开头的。
*/
- (void)testGCD1
{
//1,创建一个串行队列
//参数:队列标签,自己随意命名, 队列属性,标示队列是串行队列还是并行队列
dispatch_queue_t queue = dispatch_queue_create("wh", DISPATCH_QUEUE_SERIAL);
//2,同步执行任务
//一般只要使用“同步”执行,对于添加到队列中的任务,会立马执行
NSLog(@"开始---");
for (int i = 0; i < 10; i++) {
dispatch_sync(queue, ^{
NSLog(@"%@ %d", [NSThread currentThread], i);
});
NSLog(@"%d", i);
}
NSLog(@"完成---");
}
1.2 串行队列,异步任务
/**
串行队列:队列中的任务,顺序的 依次执行
异步执行:开辟新线程,在新线程中执行
结果:新开辟了一个线程,串行队列中的任务放在新开辟的线程中执行。
问题:串行队列,异步执行,不是应该开辟两个线程,串行队列放在新开辟的线程中执行吗?为什么新开辟了两个线程,串行队列放在新开的第二个线程中执行。
答案:number代表的并不是线程的数量,而是线程的标号,实际上只有一个线程
*/
- (void)testGCD2
{
//1,创建一个串行队列, 两种方式,第一个参数是标签,第二个参数是宏定义,打开会发现,宏定义的值是 NULL,所以创建串行队列直接写 NULL 作用是一样的。
// dispatch_queue_t queue = dispatch_queue_create("wanden", NULL);
dispatch_queue_t queue = dispatch_queue_create("walden", DISPATCH_QUEUE_SERIAL);
//2,异步执行
NSLog(@"开始任务");
for (int i = 0; i < 10; i++) {
dispatch_async(queue, ^{
NSLog(@"%@ %d", [NSThread currentThread], i);
});
}
}
1.3 并行队列,异步任务
/*
并行队列:队列中的任务可以同时在多个线程上执行
异步执行:异步,肯定会开辟新线程,在新线程中执行任务
结果:新开辟了多个线程,在多个线程上同时执行任务,主线程继续执行
**/
- (void)testGCD3
{
//1,创建一个并行队列
dispatch_queue_t queue = dispatch_queue_create("abc", DISPATCH_QUEUE_CONCURRENT);
//2,异步执行一个队列
NSLog(@"开始任务");
for (int i = 0; i < 10; i++) {
dispatch_async(queue, ^{
NSLog(@"%@ %d", [NSThread currentThread], i);
});
}
NSLog(@"任务完成 !!");
}
1.4 并行队列,同步任务
/*
并行队列:队列可以同时取出多个任务,同时在多个线程上执行
同步执行:队列中的任务只能一个一个顺序执行
结果:没有开辟新线程,队列中的任务顺序一个一个的执行
**/
- (void)testGCD4
{
//1,并行队列
dispatch_queue_t queue = dispatch_queue_create("absn", DISPATCH_QUEUE_CONCURRENT);
//2,同步执行
NSLog(@"开始执行");
for (int i = 0; i < 10; i++) {
dispatch_sync(queue, ^{
NSLog(@"%@ %d", [NSThread currentThread], i);
});
}
NSLog(@"任务完成");
}
2,主队列,全局队列,同步任务的作用
2.0 队列的选择
/**
队列的选择:
串行队列异步执行
- 开一条线程, 顺序执行
- 效率:不高,执行比较慢,资源占用小 -》 省电
一般网络是3G,对想能要求不会很高。
并发队列异步执行
- 开启多条线程,并发执行
- 效率:高,执行快,资源消耗大-》费电
使用场合:
- 网络WiFi,或者需要很快的响应,要求用户体验非常流畅。
-对任务执行顺序没有要求
-同步任务:一般只会在并发队列, 需要阻塞后续任务。必须等待同步任务执行完毕,再去执行其他任务。"依赖"关系
*/
2.1 主队列
#pragma mark----GCD 主队列
/*
主队列:主队列是串行队列,专门负责在主线程上调度任务,在主队列中不能开辟新的线程
同步执行:要求放到队列上的任务马上执行
结果:死锁
分析:主线程是一个串行队列,同步任务要求立马执行,在主线程上同步执行任务时,同步任务会等待主线程上的任务执行完成之后开始执行,而主线程又在等待同步任务执行完成之后才能继续执行,这样主线程和同步任务互相等待造成死锁。
**/
- (void)testGCD6
{
//1,获取主队列
dispatch_queue_t queue = dispatch_get_main_queue();
//2,主队列上同步执行任务
NSLog(@"开始任务");
for (int i = 0; i < 10; i++) {
dispatch_sync(queue, ^{
NSLog(@"%@ %d", [NSThread currentThread], i);
});
}
NSLog(@"任务完成");
}
/**
主队列:主队列是串行队列,专门负责在主线程上调度任务,在主队列中不能开辟新的线程
异步执行:开一开辟新线程,队列中任务在新线程中执行
结果:不开辟新线程,只能在主队列中,依次顺序执行。
个人理解:主队列是一个串行的队列,程序执行到,往主队列中添加任务之前,主队列中已经有了很多的任务,所以新加入的任务会被放到串行队列的末尾,所以,添加到主队列中的任务会等主队列上所有任务执行完成之后才会去执行。
*/
- (void)testGCD5
{
//1,获取主队列
dispatch_queue_t queue = dispatch_get_main_queue();
//2,异步执行任务
NSLog(@"开始任务----");
for (int i = 0; i < 10; i++) {
//异步:先把任务放进主队列中,但是不会马上执行。
dispatch_async(queue, ^{
NSLog(@"%@ %d", [NSThread currentThread], i);
});
}
NSLog(@"结束任务----");
}
2.2 全局队列
#pragma mark----GCD 全局队列
/**
全局队列和并行队列的区别:
1,全局队列没有名称,我们不能给命名,并发队列我们创建时候可以指定名称
2,全局队列是供所有的应用程序共享的
3,在MRC下,并行队列创建之后需要我们手动释放,全局队列不需要我们管理。
*/
- (void)testGCD8
{
//1,获得全局队列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
/*
获取全局队列时候,
第一个参数:表示优先级,默认是0,一般情况下写0,否则影响代码执行速度。 IOS7 和 IOS8 中优先级的代码是不同的,为了兼容这两个版本,一般写成0.
iOS 7
DISPATCH_QUEUE_PRIORITY_HIGH 2 高优先级
DISPATCH_QUEUE_PRIORITY_DEFAULT 0 默认优先级
DISPATCH_QUEUE_PRIORITY_LOW (-2) 低优先级
DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN 后台优先级
iOS 8
QOS_CLASS_DEFAULT 0
第二个参数:保留参数,一般也写0
*/
//2,添加异步任务
NSLog(@"开始任务");
for (int i = 0; i < 10; i++) {
dispatch_async(queue, ^{
NSLog(@"%@ %d", [NSThread currentThread], i);
});
}
NSLog(@"任务完成 !!");
}
2.3 同步任务的作用
#pragma mark----GCD 同步任务的作用
- (void)testGCD7
{
//例如,有三个任务,后两个任务必须在第一个任务完成之后才能开始并行执行
//1,创建并行队列
dispatch_queue_t queue = dispatch_queue_create("shjs", DISPATCH_QUEUE_CONCURRENT);
//2,添加任务
//2.1 第一个任务同步执行 同步执行 的任务会立即马上执行,执行完成之后才能执行其它操作
dispatch_sync(queue, ^{
NSLog(@"work 1 %@ ", [NSThread currentThread]);
});
//2.2 第二个任务异步执行
dispatch_async(queue, ^{
sleep(2);
NSLog(@"work 2 %@", [NSThread currentThread]);
});
//2.3 第三个任务异步执行
dispatch_async(queue, ^{
sleep(2);
NSLog(@"work 3 %@", [NSThread currentThread]);
});
}
3,线程间通信
耗时操作一般都放到异步线程中执行,异步执行额线程中执行完毕之后,一般需要更新UI,而更新UI只能在主线程中操作,因此,可以在异步的线程中通过这个方法来给主线程的队列中添加任务,让更新UI的操作在主线程中完成,以此实现 耗时操作异步执行,更新UI在主线程中执行。
dispatch_async(
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 执行耗时的异步操作...
dispatch_async(dispatch_get_main_queue(), ^{
// 回到主线程,执行UI刷新操作
});
});
4,GCD的其它用法
4.1 一次性操作
#pragma mark-----一次性操作
- (void)once
{
static dispatch_once_t onceToken;
NSLog(@"%ld", onceToken);
dispatch_once(&onceToken, ^{
NSLog(@"%ld", onceToken);
NSLog(@"一次性执行代码");
});
NSLog(@"Done");
}
4.2 延时操作
iOS常见的延时执行有2种方式:
调用NSObject的方法
使用GCD函数
调用NSObject的方法
[self performSelector:@selector(run) withObject:nil afterDelay:2.0];
// 2秒后再调用self的run方法
使用GCD函数
#pragma mark 延时操作
- (void)delay
{
//延时执行这个操作,系统已经帮我们写了一个block块,可以直接拿来使用
// dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(<#delayInSeconds#> * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
// <#code to be executed after a specified delay#>
// });
/**
参数:
DISPATCH_TIME_NOW 0 , 一个宏定义
NSEC_PER_SEC: 很大的数字, 单位是纳秒,1000000000ull纳秒刚好是1S
*/
dispatch_time_t when = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC));
//执行任务的队列,这个也是可以自己定义,自己去改变的。
// 参数: when : 表示从现在开始,经过多少纳秒以后
// queue : 在哪一个队列里面执行后面的任务
dispatch_after(when, dispatch_get_main_queue(), ^{
NSLog(@"yanshi caozuo");
});
}
4.3 调度组(分组)
#pragma mark - 调度组(分组)group
- (void)group
{
/**
应用场景:
开发的时候,有的时候出现多个网络请求都完成以后(每一个网络请求的事件长短不一定),再统一通知用户
比如: 下载小说:三国演义, 红楼梦, 金X梅
*/
// 实例化一个调度组
dispatch_group_t group = dispatch_group_create();
// 队列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
// 任务添加到队列queue
dispatch_group_async(group, queue, ^{
NSLog(@"下载小说A---%@", [NSThread currentThread]);
});
dispatch_group_async(group, queue, ^{
NSLog(@"下载小说B---%@", [NSThread currentThread]);
});
dispatch_group_async(group, queue, ^{
NSLog(@"下载小说X---%@", [NSThread currentThread]);
});
// 获得所有调度组里面的异步任务完成的通知
// dispatch_group_notify(group, queue, ^{
// NSLog(@"下载完成,请观看%@", [NSThread currentThread]); // 异步的
// });
//注意点: 在调度组完成通知里,可以跨队列通信
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// 更新UI,在主线程
NSLog(@"下载完成,请观看%@", [NSThread currentThread]); // 异步的
});
}
4.4 dispatch_barrier_async函数
dispatch_barrier_async函数可以调节并行队列中任务的执行顺序,如下所示的代码中,如果加上dispatch_barrier_async函数,那么第1,2个任务执行完成之后才会去执行任务3,4,不加dispatch_barrier_async函数的时候,任务1,2,3,4的执行顺序是不确定的。
- (void)barrier
{
dispatch_queue_t concurrentQueue = dispatch_queue_create("my.concurrent.queue", DISPATCH_QUEUE_CONCURRENT);
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");
});
}
4.5 dispatch_apply
#pragma mark - dispatch_apply
- (void)apply
{
//1,dispathc_apply 是dispatch_sync 和dispatch_group的关联API.它以指定的次数将指定的Block加入到指定的队列中。
NSArray *array = [NSArray arrayWithObjects:
@"/Users/chentao/Desktop/copy_res/gelato.ds",
@"/Users/chentao/Desktop/copy_res/jason.ds",
@"/Users/chentao/Desktop/copy_res/jikejunyi.ds",
@"/Users/chentao/Desktop/copy_res/molly.ds",
@"/Users/chentao/Desktop/copy_res/zhangdachuan.ds",
nil];
dispatch_async(dispatch_get_global_queue(0, 0), ^(){
dispatch_apply([array count], dispatch_get_global_queue(0, 0), ^(size_t index){
NSLog(@"%@ ---copy-%ld",[array objectAtIndex:index], index);
});
NSLog(@"done");
});
//2,index 顺序不确定,因为它是并行执行的(dispatch_get_global_queue是并行队列),但是done是在以上拷贝操作完成后才会执行,因此,它一般都是放在dispatch_async里面(异步)。实际上,这里 dispatch_apply如果换成串行队列上,则会依次输出index,但这样违背了我们想并行提高执行效率的初衷。
}
程序示例Demo