目录:
1.GCD简介
2.串行队列 + 同步执行
3.串行队列 + 异步执行
4.并发队列 + 同步执行
5.并发队列 + 异步执行
6.全局队列
7.主队列
8.线程间通信
9.GCD栅栏函数(dispatch_barrier_async)
10.GCD调度组
一.GCD简介
注意:开启线程的操作是CPU做的,GCD只负责任务的调度。
(1).GCD中涉及到两个十分重要的概念, 就是任务和队列.
任务(Task): 你需要执行的操作;
队列(Queue): 存放任务的容器。
(2).GCD使用步骤,2步。
定制任务(确定想要做的事情);
然后将任务添加到队列中( GCD会自动将队列中的任务取出来,放到对应的线程中执行。取任务遵循队列FIFO原则:先添加进队列的任务先被取出来)。
(3).GCD中的队列分2类
并发队列:可以让多个任务并发(同时)执行(能够开启多个线程同时执行任务),并发功能只有在异步执行函数下有效。
串行队列:任务只能在同一条线程中(可以是主线程,也可以是子线程)一个一个按顺序执行。
(4).任务执行分为同步执行和异步执行
同步执行 :dispatch_sync(dispatch_queue_t _Nonnull queue, ^(void)block),一个任务没有结束,就不会去执行下一个任务。同步执行函数无法开启新线程。
异步执行: dispatch_async(dispatch_queue_t _Nonnull queue, ^(void)block),不用等待任务执行完,就可以执行下一个任务。异步执行函数有能力开启新线程。
二.各种情况详细分析
(1).串行队列 + 同步执行
//1.队列 -- 串行 DISPATCH_QUEUE_SERIAL 串行 等价于NULL
dispatch_queue_t q = dispatch_queue_create("CC_GCD", NULL);
//2.先循环添加任务,同步执行任务
for (int i = 0; i < 10; i++) {
dispatch_sync(q, ^{
NSLog(@"%@,%d",[NSThread currentThread],i);
});
分析:首先创建了一个串行队列,然后循环向队列中添加10个任务,调用GCD同步执行函数执行队列中的任务。 由于是同步执行所以不会开启新的线程,任务都添加在串行队列,所以这些打印会按顺序执行,且都在主线程中执行。
(2).串行队列 + 异步执行
//1.队列 -- 串行 DISPATCH_QUEUE_SERIAL 串行 等价于NULL
dispatch_queue_t q = dispatch_queue_create("CC_GCD", NULL);
//2..先循环添加任务,异步执行任务
for (int i = 0; i < 10; i++) {
NSLog(@"%d------------- ",i);
dispatch_async(q, ^{
NSLog(@"%@,%d",[NSThread currentThread],i);
});
}
NSLog(@"come here");
分析:首先创建了一个串行队列,然后循环向队列中添加10个任务,调用GCD异步执行函数执行队列中的任务。 由于是异步执行所以会开启新的线程,任务都添加在串行队列,所以这些打印会按顺序执行,但是是在新的线程中执行,子线程只会开启1条。
(3).并发队列 + 同步执行
//1.队列 DISPATCH_QUEUE_CONCURRENT
dispatch_queue_t q = dispatch_queue_create("CC_GCD", DISPATCH_QUEUE_CONCURRENT);
//2.同步执行任务
for (int i = 0; i < 10; i++) {
NSLog(@"%d------------- ",i);
dispatch_sync(q, ^{
NSLog(@"%@,%d",[NSThread currentThread],i);
});
}
NSLog(@"come here");
分析:首先创建了一个并发队列,然后循环向队列中添加10个任务,调用GCD同步执行函数执行队列中的任务。 由于是同步执行所以不会开启新的线程,任务都添加在并发队列,虽然我们说并发队列里面的任务可以同时取出来执行,但是这里是同步执行,没有开启新的线程,所以这些打印还是会按顺序执行,且都在主线程中执行。
(4).并发队列 + 异步执行
//1.队列 DISPATCH_QUEUE_CONCURRENT
dispatch_queue_t q = dispatch_queue_create("CC_GCD", DISPATCH_QUEUE_CONCURRENT);
//2.异步执行任务
for (int i = 0; i < 10; i++) {
NSLog(@"%d------------- ",i);
dispatch_async(q, ^{
NSLog(@"%@,%d",[NSThread currentThread],i);
});
}
NSLog(@"come here");
分析:首先创建了一个并发队列,然后循环向队列中添加10个任务,调用GCD异步执行函数执行队列中的任务。 由于是异步执行所以会开启新的线程,任务都添加在并发队列,所以这十个任务能够同时执行,那么久会开启多条线程,至于开启几条线程用CPU决定,程序员无法决定。
(5).全局队列(本质上是一个并发队列)
创建全局队列函数:dispatch_get_global_queue(long identifier, unsigned long flags);
参数说明: 参数一:涉及系统适配
iOS 8.0 服务质量
QOS_CLASS_USER_INTERACTIVE 用户交互(希望线程快速执行,不要放一些耗时操作)
QOS_CLASS_USER_INITIATED 用户需要的(不要放一些耗时操作)
QOS_CLASS_DEFAULT 0 默认
QOS_CLASS_UTILITY 使用工具(用来耗时操作)
QOS_CLASS_BACKGROUND 后台
QOS_CLASS_UNSPECIFIED 没有指定优先级
iOS 7.0 调度优先级
DISPATCH_QUEUE_PRIORITY_HIGH 高优先级
DISPATCH_QUEUE_PRIORITY_DEFAULT 0 默认优先级
DISPATCH_QUEUE_PRIORITY_LOW (-2) 低优先级
DISPATCH_QUEUE_PRIORITY_BACKGROUND 后台优先级
参数二:为未来的保留参数,
提示:尤其不要选择BACKGROUND ,线程执行会慢到令人发指!
(6).主队列(本质上是一个并发队列)
创建主队列函数:dispatch_get_main_queue(void)
注:主队列上的任务只能够在主线程上执行,所以无论同步执行还是异步执行函数,都不会开启新线程。
主队列+同步执行
NSLog(@"------start-------");
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"download1-------%@",[NSThread currentThread]);
});
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"download1-------%@",[NSThread currentThread]);
});
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"download1-------%@",[NSThread currentThread]);
});
NSLog(@"-------end---------");
分析:现在是直接崩溃了, 以前这种情况是会发生死锁的。
主队列+异步函数
// 1.获得主队列
dispatch_queue_t queue = dispatch_get_main_queue();
NSLog(@"-----start------");
// 2.将任务封装到异步函数中 ,添加任务到主队列中
dispatch_async(queue, ^{
NSLog(@"download1 ----- %@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"download2 ----- %@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"download3 ----- %@", [NSThread currentThread]);
});
NSLog(@"-----end------");
分析:不开线程,start和end执行完后,主队列中的任务串行在主线程执行。
(7). 线程间通信
// 1. 创建子线程处理耗时操作
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//1.1耗时操作开始
for (int i = 0; i < 10000; i++) {
NSLog(@"---耗时操作-%@----%d", [NSThread currentThread],i);
}
//1.2耗时操作完成回到主线程中
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"----%@----", [NSThread currentThread]);
});
});
(8)GCD栅栏函数(dispatch_barrier_async)
dispatch_barrier_async函数的作用与barrier的意思相同,在进程管理中起到一个栅栏的作用,它等待所有位于barrier函数之前的操作执行完毕后执行,并且在barrier函数执行之后,barrier函数之后的操作才会得到执行,该函数需要同dispatch_queue_create函数生成的concurrent Dispatch Queue队列一起使用。
例:
输出结果:
分析:前十个 --> barrier -->后十个 其中 前十个 与 后十个 由于并行处理先后顺序不定
运用场景:用barrier实现: 读-写锁,读-写锁有以下几个特点: 在没有写操作的时候, 可以任意的并发读取, 在所有读操作完成后, 才进行写操作, 但是写操作不可以并发, 且在写操作过程中, 不能读取, 在写操作完成后, 又可以任意的并发读取了。如果我们使用barrier GCD接口来处理写操作, 使用普通的GCD接口来并发读取, 那么完全满足读-写锁的以上特点
dispatch_barrier_sync和dispatch_barrier_async的区别:
在将任务插入到queue的时候,dispatch_barrier_sync需要等待自己的任务结束之后才会继续程序,然后插入被写在它后面的任务,然后执行后面的任务
而dispatch_barrier_async将自己的任务插入到queue之后,不会等待自己的任务结束,它会继续把后面的任务插入到queue
所以,dispatch_barrier_async的不等待(异步)特性体现在将任务插入队列的过程,它的等待特性体现在任务真正执行的过程。
(9).GCD调度组
使用场景:在实际开发中,需要开启N个异步线程,但是后续操作,需要依赖N个线程返回的数据,需要接收所有线程任务执行完成的通知。
例:
#pragma mark - 调度组
- (void)group{
/*
*应用场景:
开发的时候,有时候出现多个网络请求都完成以后(每个网络请求时间的时长不一样),再通知用户
比如下载书:三国演义,红楼梦,西游记
*/
//1.实例化一个调度组
dispatch_group_t group = dispatch_group_create();
//2.队列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
//3.任务添加到队列queue
dispatch_group_async(group, queue, ^{
for (int i = 0; i<=50; i++) {
if (i == 50) {
NSLog(@"---下载小说A:%@",[NSThread currentThread]);
}
}
});
dispatch_group_async(group, queue, ^{
for (int i = 0; i<=50; i++) {
if (i == 50) {
NSLog(@"---下载小说B:%@",[NSThread currentThread]);
}
}
});
dispatch_group_async(group, queue, ^{
for (int i = 0; i<=500; i++) {
if (i == 50) {
NSLog(@"---下载小说C:%@",[NSThread currentThread]);
}
}
});
//4.获得所有调度组里面的异步任务完成的通知<上面3本书都下载完了才会执行>
dispatch_group_notify(group, queue, ^{
NSLog(@"--- 下载完成的操作:%@",[NSThread currentThread]);
});
}
输出结果: