1.GCD - Grand Central Dispatch 纯C语言;苹果为多核处理器的并行运算提供的解决方案,自动调度cpu内核,自动管理线程生命周期(创建线程、调度任务、销毁线程)。
2.任务、队列
任务:要执行的操作(逻辑代码)
队列:存放任务。GCD会自动从队列中取出任务,在相应的线程中执行,遵循FIFO原则,先进先出,后进后出
3.执行任务:同步、异步
同步:不能开启新线程
异步:可以开启新线程,可以创建多条线程,并发执行任务
4.队列类型:并发、串行
并发(Concurrent Dispatch Queue):多个任务同时执行,同时开启多个线程,每个线程执行一个任务,但是只能在异步下有效
串行(Serial Dispatch Queue):多个任务排队按顺序执行
5.任务与队列的四种组合方式:同步并发、同步串行、异步并发、异步串行。
5.1 同步并发代码:
// 1.获得全局的并发队列,供整个应用使用,不用手动创建
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 2.将任务加入队列
dispatch_sync(queue, ^{
NSLog(@"1-----%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"2-----%@", [NSThread currentThread]);
});
控制台输出:
2016-08-06 11:48:17.716 07-GCD的基本使用[63024:4677093] 1-----{number = 1, name = main}
2016-08-06 11:48:17.717 07-GCD的基本使用[63024:4677093] 2-----{number = 1, name = main}
--- 对全局并发队列参数做一解释:
第一个参数--队列优先级:默认使用第二个
#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
第二个参数--unsigned long flags,此参数作为以后的扩展参数使用,目前传0就可以
5.2 同步串行:不开启新线程,在当前线程执行任务,排队执行,一个接一个
// 1.创建串行队列
dispatch_queue_t queue = dispatch_queue_create("etong.queue", DISPATCH_QUEUE_SERIAL);
// 2.将任务加入队列
dispatch_sync(queue, ^{
NSLog(@"1-----%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"2-----%@", [NSThread currentThread]);
});
控制台输出:
2016-08-06 14:22:40.753 07-GCD的基本使用[63168:4815767] 1-----{number = 1, name = main}
2016-08-06 14:22:40.753 07-GCD的基本使用[63168:4815767] 2-----{number = 1, name = main}
---对手动创建线程参数做出解释:
dispatch_queue_create(const char *label, dispatch_queue_attr_t attr);
const char *label -- 线程标识,随便写一个唯一的 - 例如:"etong.queue"
dispatch_queue_attr_t attr -- 队列属性
#define DISPATCH_QUEUE_SERIAL NULL
#define DISPATCH_QUEUE_CONCURRENT
所以创建队列也可以这么写:dispatch_queue_t queue = dispatch_queue_create("etong.queue", NULL);
5.3 异步并发:可同时开始多条线程,可以手动创建、也可以使用全局并发队列
// 1.创建一个并发队列
// label : 相当于队列的名字
// dispatch_queue_t queue = dispatch_queue_create("etong.queue", DISPATCH_QUEUE_CONCURRENT);
// 1.获得全局的并发队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 2.将任务加入队列
dispatch_async(queue, ^{
for (NSInteger i = 0; i<3; i++) {
NSLog(@"1-----%@", [NSThread currentThread]);
}
});
dispatch_async(queue, ^{
for (NSInteger i = 0; i<3; i++) {
NSLog(@"2-----%@", [NSThread currentThread]);
}
});
控制台输出:
2016-08-06 14:35:16.894 07-GCD的基本使用[63188:4876010] 1-----{number = 2, name = (null)}
2016-08-06 14:35:16.894 07-GCD的基本使用[63188:4876024] 2-----{number = 3, name = (null)}
2016-08-06 14:35:16.895 07-GCD的基本使用[63188:4876010] 1-----{number = 2, name = (null)}
2016-08-06 14:35:16.895 07-GCD的基本使用[63188:4876024] 2-----{number = 3, name = (null)}
2016-08-06 14:35:16.895 07-GCD的基本使用[63188:4876010] 1-----{number = 2, name = (null)}
2016-08-06 14:35:16.896 07-GCD的基本使用[63188:4876024] 2-----{number = 3, name = (null)}
要说明的是:异步并发函数块执行代码的顺序--是将函数块的代码执行完之后,才会返回去执行队列中任务。--重要
5.4 异步串行 会开启新的线程,任务还是排队依次执行
// 1.创建串行队列
dispatch_queue_t queue = dispatch_queue_create("etong.queue", DISPATCH_QUEUE_SERIAL);
// dispatch_queue_t queue = dispatch_queue_create("etong.queue", NULL);
// 2.将任务加入队列
dispatch_async(queue, ^{
NSLog(@"1-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"2-----%@", [NSThread currentThread]);
});
控制台输出:
2016-08-06 14:40:40.694 07-GCD的基本使用[63202:4904803] 1-----{number = 2, name = (null)}
2016-08-06 14:40:40.694 07-GCD的基本使用[63202:4904803] 2-----{number = 2, name = (null)}
*还有2种主队列的情况
5.5 异步+主队列:只在主线程中执行任务
// 1.获得主队列
dispatch_queue_t queue = dispatch_get_main_queue();
// 2.将任务加入队列
dispatch_async(queue, ^{
NSLog(@"1-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"2-----%@", [NSThread currentThread]);
});
控制台输出:
2016-08-06 14:43:13.921 07-GCD的基本使用[63217:4920120] 1-----{number = 1, name = main}
2016-08-06 14:43:13.921 07-GCD的基本使用[63217:4920120] 2-----{number = 1, name = main}
5.6 同步+主队列:这个比较奇葩
NSLog(@"++++++++++++");
// 1.获得主队列
dispatch_queue_t queue = dispatch_get_main_queue();
// 2.将任务加入队列
dispatch_sync(queue, ^{
NSLog(@"1-----%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"2-----%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"3-----%@", [NSThread currentThread]);
});
NSLog(@"-------------");
控制台输出:
2016-08-06 14:44:50.115 07-GCD的基本使用[63229:4929440] ++++++++++++
-- 对同步+主队列输出解释一下:因为是同步,所以等待任务依次执行,但对于开启的多个任务来说,需要等待函数块执行完,执行队列中的任务,这样双方都在等待,导致程序卡死不动。
6.线程间通信:基本上是子线程下载的数据需要在主线程完成加载刷新
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 图片的网络路径
NSURL *url = [NSURL URLWithString:@"https://ss0.baidu.com/6ONWsjip0QIZ8tyhnq/it/u=1537639143,1137876288&fm=58"];
// 加载图片
NSData *data = [NSData dataWithContentsOfURL:url];
// 生成图片
UIImage *image = [UIImage imageWithData:data];
// 回到主线程
dispatch_async(dispatch_get_main_queue(), ^{
self.imageView.image = image;
});
});
7.其他GCD函数的使用
7.1 延时执行:有三种延时执行的方法
7.1.1 调用NSObject方法
[self performSelector:@selector(run) withObject:nil afterDelay:2.0];
7.1.2 使用GCD函数
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"run-----");
});
解释一下:2.0是需要延时的秒数;dispatch_get_main_queue()根据需要创建队列
7.1.3 调用NSTimer方法
[NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:NO];
7.2 一次性GCD函数:保证block中代码在程序运行期间只被执行一次
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSLog(@"------run");//只执行一次,默认是线程安全的
});
7.3 拦截函数
dispatch_queue_t queue = dispatch_queue_create("123456", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"----1-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"----2-----%@", [NSThread currentThread]);
});
dispatch_barrier_async(queue, ^{
NSLog(@"----barrier-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"----3-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"----4-----%@", [NSThread currentThread]);
});
控制台输出:
2016-08-06 15:06:40.230 09-掌握-GCD的其他常用函数[63329:5037819] ----1-----{number = 2, name = (null)}
2016-08-06 15:06:40.230 09-掌握-GCD的其他常用函数[63329:5037834] ----2-----{number = 3, name = (null)}
2016-08-06 15:06:40.231 09-掌握-GCD的其他常用函数[63329:5037834] ----barrier-----{number = 3, name = (null)}
2016-08-06 15:06:40.231 09-掌握-GCD的其他常用函数[63329:5037834] ----3-----{number = 3, name = (null)}
2016-08-06 15:06:40.231 09-掌握-GCD的其他常用函数[63329:5037819] ----4-----{number = 2, name = (null)}
--解释一下:barrier-栅栏,起拦截作用,他前面的任务执行完毕才能继续往下执行。
此方法创建的队列只能是手动创建,不能使用全局并发队列,否则不起作用
7.4 队列组:需求--分别异步执行耗时操作,完毕后,回到主线程操作
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 创建一个队列组
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, queue, ^{
//执行一个耗时操作
});
dispatch_group_async(group, queue, ^{
//执行一个耗时操作
});
dispatch_group_notify(group, queue, ^{
dispatch_async(dispatch_get_main_queue(), ^{
//回到主线程
});
});
7.5 快速迭代函数:此方法可以在多条线程下同时完成任务
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_apply(subpaths.count, queue, ^(size_t index) {
});
-- 对快速迭代函数做出解释:for循环遍历10个变量的话,是顺序挨个遍历;而GCD快速迭代是同时开始10个线程一次遍历完,几乎是同时的。
网友评论