分享是每个优秀的程序员所必备的品质
简介
GCD
- 全称是Grand Central Dispatch :伟大的中枢调度器
- 纯C语言,提供了非常多强大的函数
优势
- GCD是苹果公司为多核的并行运算提出的解决方案
- GCD会自动利用更多的CPU内核(比如双核、四核)
- GCD会自动管理线程的生命周期(创建线程、调度任务、销毁线程)
- 程序员只需要告诉GCD想要执行什么任务,不需要编写任何线程管理代码
核心概念
- 任务:执行什么操作
- 队列:用来存放任务
队列
并发队列(Concurrent Dispatch Queue)
- 可以让多个任务并发(同时)执行(自动开启多个线程同时执行任务)
- 并发功能只有在异步(dispatch_async)函数下才有效
串行队列(Serial Dispatch Queue)
- 让任务一个接着一个地执行(一个任务执行完毕后,再执行下一个任务)
GCD的使用步骤
1、定制任务,确定想做的事情
2、将任务添加到队列中,GCD会自动将队列中的任务取出,放到对应的线程中执行,任务的取出遵循队列的FIFO原则:先进先出,后进后出
并发队列
使用dispatch_queue_create
函数创建队列
// 参数1:队列名称,注意传c字符 如:"RC"
// 参数2:队列的类型 DISPATCH_QUEUE_CONCURRENT:并发 ,SERIAL:串行
dispatch_queue_t queue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL);
GCD默认已经提供了全局的并发队列,供整个应用使用,可以无需手动创建
使用dispatch_get_global_queue
函数获得全局的并发队列
// 参数1: 队列的优先级 dispatch_queue_priority_t
// 参数2: unsigned long flags ,此参数暂时无用,用0即可
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
队列有四个优先级
#define DISPATCH_QUEUE_PRIORITY_HIGH 2 //高
#define DISPATCH_QUEUE_PRIORITY_DEFAULT 0 // 默认的(中)
#define DISPATCH_QUEUE_PRIORITY_LOW (-2) // 低
#define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN // 后台
补充:全局队列在整个应用程序中是默认存在的,并且对应有高、默认、低和后台优先级一共四个并发队列,使用的时候只是选择其中一个直接拿来使用。而使用create是手动创建一个新的队列。
串行队列队列
GCD中获得串行有2种途径
1、使用dispatch_queue_create
函数创建串行队列
// 创建串行队列(队列类型传递NULL或者DISPATCH_QUEUE_SERIAL)
dispatch_queue_t queue = dispatch_queue_create("RC", NULL);
2、使用主队列(跟主线程相关联的队列)
- 主队列是GCD自带的一种特殊的串行队列
- 放在主队列中的任务,都会放到主线程中执行
使用dispatch_get_main_queue()
获得主队列
dispatch_queue_t queue = dispatch_get_main_queue()
GCD基本使用
- 异步函数+并发队列 :开启多条线程,并发执行任务
- (void)asyncConcurrent{
// 1.创建队列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
NSLog(@"---satrt----");
// 2.添加任务
dispatch_async(queue, ^{
NSLog(@"1----%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"2----%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"3----%@",[NSThread currentThread]);
});
NSLog(@"---end----");
}
打印:
RCGCDDemo[3650:175994] ---satrt----
RCGCDDemo[3650:175994] ---end----
RCGCDDemo[3650:176035] 2----{number = 3, name = (null)}
RCGCDDemo[3650:176114] 1----{number = 4, name = (null)}
RCGCDDemo[3650:176115] 3----{number = 5, name = (null)}
注意:并不是一个任务对应开启一条线程,具体需要开启多少条线程由系统决定
2.异步函数+串行队列:开启一条线程,串行执行任务
- (void)asyncSerial{
//1.创建队列
dispatch_queue_t queue = dispatch_queue_create("RC", DISPATCH_QUEUE_SERIAL);
NSLog(@"---satrt----");
dispatch_async(queue, ^{
NSLog(@"1----%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"2----%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"3----%@",[NSThread currentThread]);
});
NSLog(@"---end----");
}
打印:
RCGCDDemo[3765:181439] ---satrt----
RCGCDDemo[3765:181439] ---end----
RCGCDDemo[3765:181473] 1----{number = 3, name = (null)}
RCGCDDemo[3765:181473] 2----{number = 3, name = (null)}
RCGCDDemo[3765:181473] 3----{number = 3, name = (null)}
3 .同步函数+并发队列:不开线程,串行执行任务
-(void)syncConcurrent{
dispatch_queue_t queue = dispatch_queue_create(NULL, DISPATCH_QUEUE_CONCURRENT);
NSLog(@"---start---");
dispatch_sync(queue, ^{
NSLog(@"1----%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"2----%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"3----%@",[NSThread currentThread]);
});
NSLog(@"---end----");
}
打印:
RCGCDDemo[3825:184734] ---start---
RCGCDDemo[3825:184734] 1----{number = 1, name = main}
RCGCDDemo[3825:184734] 2----{number = 1, name = main}
RCGCDDemo[3825:184734] 3----{number = 1, name = main}
RCGCDDemo[3825:184734] ---end----
4.同步函数+串行队列:不开线程,串行执行任务
-(void)syncSerial{
dispatch_queue_t queue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL);
NSLog(@"---start---");
dispatch_sync(queue, ^{
NSLog(@"1----%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"2----%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"3----%@",[NSThread currentThread]);
});
NSLog(@"---end----");
}
打印:
RCGCDDemo[3909:189316] ---start---
RCGCDDemo[3909:189316] 1----{number = 1, name = main}
RCGCDDemo[3909:189316] 2----{number = 1, name = main}
RCGCDDemo[3909:189316] 3----{number = 1, name = main}
RCGCDDemo[3909:189316] ---end----
5.异步函数+主队列:不开线程,在主线程中串行执行任务
//异步函数+主队列
-(void)asyncMain{
dispatch_queue_t queue = dispatch_get_main_queue();
NSLog(@"---start---");
dispatch_async(queue, ^{
NSLog(@"1----%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"2----%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"3----%@",[NSThread currentThread]);
});
NSLog(@"---end----");
}
打印:
RCGCDDemo[4050:195922] ---start---
RCGCDDemo[4050:195922] ---end----
RCGCDDemo[4050:195922] 1----{number = 1, name = main}
RCGCDDemo[4050:195922] 2----{number = 1, name = main}
RCGCDDemo[4050:195922] 3----{number = 1, name = main}
6.同步函数+主队列:不开线程,串行执行任务(注意死锁发生)
注意:该方法在主线程中执行发生死锁;该方法在子线程中执行,那么所有的任务在主线程中执行。
-(void)syncMain{
dispatch_queue_t queue = dispatch_get_main_queue();
NSLog(@"start----");
dispatch_sync(queue, ^{
NSLog(@"1----%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"2----%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"3----%@",[NSThread currentThread]);
});
NSLog(@"end---");
}
在主线程中执行-(void)syncMain
方法,会因为相互等待而发生死锁,程序崩溃!
如果在主线程执行该方法,该任务1、2、3因为加入了主队列所以也会在主线程中执行。当执行到任务1的时候,因为当前主线程正在等待任务1、2、3执行结束,才会再执行其他任务,这就造成了任务1等待-(void)syncMain
方法执行结束,而-(void)syncMain
需要方法里面的所以任务执行完毕才会结束(需要任务1、2、3执行完毕),-(void)syncMain
和任务1相互等待造成死锁!!!
在子线程中执行的打印:
RCGCDDemo[4104:198438] start----
RCGCDDemo[4104:198392] 1----{number = 1, name = main}
RCGCDDemo[4104:198392] 2----{number = 1, name = main}
RCGCDDemo[4104:198392] 3----{number = 1, name = main}
RCGCDDemo[4104:198438] end---
GCD线程间通信
以在子线程下载,主线程显示为例
- (void)laodImg{
dispatch_async(dispatch_get_global_queue(0, 0), ^{
// 下载图片操作
dispatch_sync(dispatch_get_main_queue(), ^{
// 显示图片
});
});
}
GCD常用函数
延迟函数
- (void)afterDelay{
//1. 延迟执行的第一种方法
//[self performSelector:@selector(task) withObject:nil afterDelay:1.0];
//2.延迟执行的第二种方法
//[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(task) userInfo:nil repeats:YES];
//3.GCD
/* 参数1: DISPATCH_TIME_NOW 从现在开始计算时间
* 参数2: 延迟的时间 1.0 GCD时间单位:纳秒
* 参数3: 队列
*/
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), queue, ^{
//
});
}
栅栏函数(控制任务的执行顺序)
有两个函数:dispatch_barrier_async
和dispatch_barrier_sync
注意:官方说明在使用栅栏函数时,使用自定义队列才有意义,如果用的是串行队列或者系统提供的全局并发队列,这个栅栏函数的作用等同于一个同步函数
dispatch_barrier_async
代码:
- (void)barrierAsync{
dispatch_queue_t queue = dispatch_queue_create(NULL, DISPATCH_QUEUE_CONCURRENT);
NSLog(@"---satrt----");
dispatch_async(queue, ^{
NSLog(@"1----%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"2----%@",[NSThread currentThread]);
});
dispatch_barrier_async(queue, ^{
NSLog(@"3----%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"4----%@",[NSThread currentThread]);
});
NSLog(@"---end----");
}
打印:
RCGCDDemo[4559:227700] ---satrt----
RCGCDDemo[4559:227700] ---end----
RCGCDDemo[4559:230128] 2----{number = 8, name = (null)}
RCGCDDemo[4559:228512] 1----{number = 5, name = (null)}
RCGCDDemo[4559:228512] 3----{number = 5, name = (null)}
RCGCDDemo[4559:228512] 4----{number = 5, name = (null)}
可得:
- barrier前面的任务都是并发执行的
- 执行到barrier时,不用执行完barrier,就可以将后面的任务插入到队列中,但是仍然需要执行完barrier,才会执行后面的任务。
dispatch_barrier_sync
- (void)barrierSync{
dispatch_queue_t queue = dispatch_queue_create(NULL, DISPATCH_QUEUE_CONCURRENT);
NSLog(@"---satrt----");
dispatch_async(queue, ^{
NSLog(@"1----%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"2----%@",[NSThread currentThread]);
});
dispatch_barrier_sync(queue, ^{
NSLog(@"3----%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"4----%@",[NSThread currentThread]);
});
NSLog(@"---end----");
}
打印:
RCGCDDemo[4653:234416] ---satrt----
RCGCDDemo[4653:234775] 1----{number = 5, name = (null)}
RCGCDDemo[4653:235167] 2----{number = 8, name = (null)}
RCGCDDemo[4653:234416] 3----{number = 1, name = main}
RCGCDDemo[4653:234416] ---end----
RCGCDDemo[4653:235167] 4----{number = 8, name = (null)}
可得:
- 同样,barrier前面的任务都是并发执行的
- 运行到barrier时,必须等到barrier执行结束后才会将后面的任务插入到队列中,继续执行。
一次性代码
注意:不能放到懒加载,主要用于单例类 : iOS单例类详解
快速迭代
开多个线程并发完成迭代操作
- (void)applyTest{
// 开子线程和主线程一起完成遍历任务,任务的执行时并发的
/*
参数1: 遍历的次数
参数2: 队列(并发队列)
参数3: index 索引
*/
dispatch_apply(5, dispatch_get_global_queue(0, 0), ^(size_t index) {
//添加耗时操作,效果更明显
for(NSInteger i=0;i<100000;i++){
}
NSLog(@"%zd---%@",index,[NSThread currentThread]);
});
}
打印:
RCGCDDemo[4969:248588] 0---{number = 1, name = main}
RCGCDDemo[4969:248654] 2---{number = 3, name = (null)}
RCGCDDemo[4969:248652] 1---{number = 4, name = (null)}
RCGCDDemo[4969:248655] 3---{number = 5, name = (null)}
RCGCDDemo[4969:248588] 4---{number = 1, name = main}
队列组
作用与栅栏函数相似:当队列组中所有的任务都执行完毕的时候,会发出通知
dispatch_group_t group = dispatch_group_create();
创建创建队列组,
dispatch_group_notify
拦截队列组中所有任务完成的通知
代码:
- (void)groupTest{
// 1.创建队列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
// 2.创建队列组
dispatch_group_t group = dispatch_group_create();
NSLog(@"---satrt----");
// 3. 添加任务,并监听任务的执行情况,通知group
dispatch_group_async(group, queue, ^{
NSLog(@"1----%@",[NSThread currentThread]);
});
dispatch_group_async(group, queue, ^{
NSLog(@"2----%@",[NSThread currentThread]);
});
dispatch_group_async(group, queue, ^{
NSLog(@"3----%@",[NSThread currentThread]);
});
//拦截通知,当队列组中所有的任务都执行完毕的时候回进入到下面的方法
dispatch_group_notify(group, queue, ^{
NSLog(@"-------come,baby");
});
NSLog(@"---end----");
}
打印:
RCGCDDemo[934:25071] ---satrt----
RCGCDDemo[934:25071] ---end----
RCGCDDemo[934:25126] 1----{number = 5, name = (null)}
RCGCDDemo[934:25125] 3----{number = 4, name = (null)}
RCGCDDemo[934:25124] 2----{number = 6, name = (null)}
RCGCDDemo[934:25124] -------come,baby
可知:使用dispatch_group_notify
是可以监听队列组中的异步函数执行情况,并且本身也是异步执行的,不会阻塞线程。
还可以使用dispatch_group_enter
和dispatch_group_leave
来达到相同的功能。dispatch_group_enter
可以使得后面的异步任务会被纳入到队列组的监听范围,进入群组,两个方法必须要配对使用
代码:
- (void)groupTest2{
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_group_t group = dispatch_group_create();
NSLog(@"---satrt----");
//后面的异步任务会被纳入到队列组的监听范围
dispatch_group_enter(group);
dispatch_async(queue, ^{
NSLog(@"1----%@",[NSThread currentThread]);
//离开群组
dispatch_group_leave(group);
});
dispatch_group_enter(group);
dispatch_async(queue, ^{
NSLog(@"2----%@",[NSThread currentThread]);
dispatch_group_leave(group);
});
dispatch_group_enter(group);
dispatch_async(queue, ^{
NSLog(@"3----%@",[NSThread currentThread]);
dispatch_group_leave(group);
});
//拦截通知,当队列组中所有的任务都执行完毕的时候回进入到下面的方法
dispatch_group_notify(group, queue, ^{
NSLog(@"-------come,baby");
});
NSLog(@"---end----");
}
打印:
RCGCDDemo[993:28534] ---satrt----
RCGCDDemo[993:28534] ---end----
RCGCDDemo[993:28621] 1----{number = 3, name = (null)}
RCGCDDemo[993:28868] 3----{number = 5, name = (null)}
RCGCDDemo[993:28867] 2----{number = 4, name = (null)}
RCGCDDemo[993:28867] -------come,baby
还可以使用dispatch_group_wait
来监听队列组中任务的执行情况,本身是阻塞的,直到队列组中所有的任务执行结束后才会执行后面方法。
// 代码同上
// 阻塞的 直到队列组中所有的任务都执行完毕之后才能执行
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
NSLog(@"---end----");
打印:
RCGCDDemo[1054:32183] ---satrt----
RCGCDDemo[1054:32242] 1----{number = 3, name = (null)}
RCGCDDemo[1054:32241] 3----{number = 6, name = (null)}
RCGCDDemo[1054:32240] 2----{number = 4, name = (null)}
RCGCDDemo[1054:32183] ---end----
可知:等任务1、2、3全部执行完毕后,才会打印后面的 “end”。