GCD(基于C语言的底层API,用Block定义任务)
GCD的核心概念
- 将任务添加到队列,并且指定执行任务的函数
- 任务使用 block 封装
- 任务的 block 没有参数也没有返回值
- 执行任务的函数
- 异步 dispatch_async
- 不用等待当前语句执行完毕,就可以执行下一条语句
- 会开启线程执行 block 的任务
- 异步是多线程的代名词
- 同步 dispatch_sync
- 必须等待当前语句执行完毕,才会执行下一条语句
- 不会开启线程
- 在当前执行 block 的任务
- 队列 - 负责调度任务
- 串行队列
- 一次只能"调度"一个任务
- dispatch_queue_create("serial", NULL);
- 并发队列
- 一次可以"调度"多个任务
- dispatch_queue_create("concurrent", DISPATCH_QUEUE_CONCURRENT);
- 主队列
- 专门用来在主线程上调度任务的队列
- 不会开启线程
- 在主线程空闲时才会调度队列中的任务在主线程执行
- dispatch_get_main_queue();
GCD 演练
1.同步串行队列
- 任务会在当前的线程中执行,会阻塞当前的线程
- 开发中很少使用
#pragma mark 同步串行队列
void gcddemo1(){
// 1.创建一个串行队列,意味着说队列里的任务是一个接着一个执行(类似于排队跑步)dispatch_queue_t q = dispatch_queue_create("serial", DISPATCH_QUEUE_SERIAL);
// 2.往 ‘队列中’ 添加 ‘任务’,这个任务是同步的,//同步意味着任务的执行是在当前线程中,如果当前是主线程,任务就在主线程中运行
for (int i = 0; i < 10; i++) {dispatch_sync(q, ^{
NSLog(@"%@ 任务%d",[NSThread currentThread],i);});
}}
2.异步串行队列
- 任务不在当前的线程中执行,会在子线程中执行
- 因为是串行队列,所以会依次执行任务
#pragma mark 异步串行队列
void gcddemo2(){
// 1.创建一个串行队列,意味着说队列里的任务是一个接着一个执行(类似于排队跑步)dispatch_queue_t q = dispatch_queue_create("gz.itcast.cn", DISPATCH_QUEUE_SERIAL);
// 2.往 ‘队列中’ 添加 ‘任务’,这个任务是异步的,// 异步意味着任务的执行会开启新线程来执行,我们称为子线程
for (int i = 0; i < 10; i++) {dispatch_async(q, ^{
NSLog(@"%@ 任务%d",[NSThread currentThread],i);});
}}
3.同步并发队列
- 任务会在当前的线程中执行,开发中很少使用
- 因为是同步执行,所以队列中的任务会依次执行
#pragma mark 同步并行队列
void gcddemo3(){
// 1.创建一个并行队列,意味着说队列里的任务是抢着执行(类似于并排跑步比赛)dispatch_queue_t q = dispatch_queue_create("gz.itcast.cn", DISPATCH_QUEUE_CONCURRENT);
// 2.往 ‘队列中’ 添加 ‘任务’,这个任务是同步的,同步意味着代码的执行是在当前线程// 但是在同步任务中,并行队列里的任务会依序执行for (int i = 0; i < 10; i++) {
dispatch_sync(q, ^{NSLog(@"%@ 任务%d",[NSThread currentThread],i);
});}
}
4.异步并发队列
- 任务不在当前的线程中执行,会在子线程中执行
- 任务会抢占式地去执
#pragma mark 异步并行队列
void gcddemo4(){
// 1.创建一个并行队列,意味着说队列里的任务是抢着执行(类似于并排跑步比赛)dispatch_queue_t q = dispatch_queue_create("gz.itcast.cn", DISPATCH_QUEUE_CONCURRENT);
// 2.往 ‘队列中’ 添加 ‘任务’,这个任务是异步的,异步意味着任务的执行是在子线程// 在异步任务中,并行队列里的任务会抢占式执行// 线程的开销由系统调试,任务的执行顺序是不同的for (int i = 0; i < 10; i++) {
dispatch_async(q, ^{
NSLog(@"%@ 任务%d",[NSThread currentThread],i);});
}}
5.全局队列(和并发队列的执行效果相同)
- 苹果为了方便多线程序的设置,提供一个全局队列,供所有的APP共同使用(日常开发中,建议使用全局队列)
- 和并发队列的区别:
- 直接get使用
- 并发队列可以设置名字,但是全局对不可以
#pragma mark 全局队列
void gcddemo5(){
// 1.获取全局队列,全局队列供所有APP使用dispatch_queue_t q = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 2.往 ‘全局队列’ 添加 ‘任务’// 2.1异步里添加同步dispatch_async(q, ^{
NSLog(@"异步任务 %@ ",[NSThread currentThread]);for (int i = 0; i < 10; i++) {
//在同步任务中,全局队列里的任务会顺序执行dispatch_sync(q, ^{
NSLog(@"%@ 任务%d",[NSThread currentThread],i);});
}});
//2.2同步里添加异步dispatch_sync(q, ^{
NSLog(@"同步任务 %@ ",[NSThread currentThread]);for (int i = 0; i < 10; i++) {
//在异步任务中,全局队列里的任务会抢战执行dispatch_async(q, ^{
NSLog(@"%@ 任务%d",[NSThread currentThread],i);});
}});
}
6.主队列
- 主线程中不能使用同步,那样会造成线程阻塞
7.Barrier 异步
- 使用 dispatch_barrier_async 添加的 block 会在之前添加的 block 全部运行结束之后,才在同一个线程顺序执行,从而保证对非线程安全的对象进行正确的操作
- dispatch_barrier 必须使用自定义的并发队列
- 举例说明:下载图片,并将下载好的图片添加到数组(可变的)里面,可变数组是线程不安全的,有可能出现多个线程同时向数组里面添加下载好的图片,这样有可能出现程序崩溃的情况
8.延迟执行
- 调用NSObject的方法
[self performSelector:@selector(run) withObject:nil afterDelay:2.0];
- 使用GCD函数
dispatch_after(dispatch_time(DISPATCH_TIME_NOW,
(int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(),^{
// 2秒后异步执行这里的代码...
});
9.调度组
- 假如有3个异步操作要先执行完,然后再到主线程中去做操作,此时使用调度组的效率是最高的。
// 1. 调度组
dispatch_group_t group = dispatch_group_create();
// 2. 队列
dispatch_queue_t q = dispatch_get_global_queue(0, 0);
// 3. 将任务添加到队列和调度组
dispatch_group_async(group, q, ^{
[NSThread sleepForTimeInterval:1.0];
NSLog(@"任务 1 %@", [NSThread currentThread]);
});
dispatch_group_async(group, q, ^{
NSLog(@"任务 2 %@", [NSThread currentThread]);
});
dispatch_group_async(group, q, ^{
NSLog(@"任务 3 %@", [NSThread currentThread]);
});
// 4. 监听所有任务完成
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"OVER %@", [NSThread currentThread]);
});
// 5. 判断异步
NSLog(@"come here");
自我小结
- 异不异步决定是否是多线程
- 异步不能确定任务的执行顺序
- 串联:任务一个一个地执行
- 并发:多个任务会一起执行
- 阻塞:使用嵌套:(串行 同步+同步) 会造成线程阻塞,因为第一个同步操作里面的任务会等第二同步操作里面的任务执行完在执行,而第二个同步操作里面的任务又会等第一个同步操作里面的任务完成,这就产生了循环,两个操作都不会执行完成。