11--多线程02--GCD初探
2021-07-04 本文已影响0人
修_远
[TOC]
(一)GCD 介绍
1.1 GCD简介
- 什么是GCD?
- 全称是 Grand Central Dispatch
- 纯 C 语言,提供了非常多强大的函数
- GCD的优势
- GCD 是苹果公司为多核的并行运行提出的解决方案
- GCD 会自动利用更多的 CPU 内核(比如双核、四核)
- GCD 会自动管理线程的声明周期(创建线程、调度任务、销毁线程)
- 程序员只需要告诉 GCD 想要执行什么任务,不需要编写任何线程管理代码
1.2 函数
将任务添加到队列,并且制定执行任务的函数
-
异步
dispatch_async
- 任务使用 block 封装
- 任务的 block 没有参数也没有返回值
- 执行任务的函数
- 不用等待当前语句执行完毕,就可以执行下一条语句
- 会开启线程执行 block 的任务
- 异步是多线程的代名词
-
同步
dispatch_sync
- 必须等待当前语句执行完毕,才会执行下一条语句
- 不会开启线程
- 在当前执行 block 的任务
1.3 队列
队列- 队列是一种数据结构,具有先进先出(FIFO)的特性
- 串行队列是按顺序执行任务的
- 并发队列中任务执行的数据不确定
- 有可能任务4最先执行完,因为它最简单,
异步任务的执行速度跟任务的复杂度有关
- 有可能任务4最先执行完,因为它最简单,
iOS系统提供了主队列和全局队列
-
主队列
dispatch_get_main_queue()
如果当前主线程正在有任务执行,那么无论主队列中当前被添加了什么任务,都不会被调度
- 专门用来在主线程上调度任务的队列
- 不会开启线程
- UI为什么要在主线程上刷新?
- 安全+效率:UIKit不是线程安全的,当多个线程同时操作UI时,资源抢夺导致崩溃,UI异常等问题;
- 用户体验:子线程中一般都会运行很多耗时的后台任务,有可能会影响UI的刷新;
-
全局队列
dispatch_get_global_queue(0, 0)
全局队列是一个并发队列
- 为了方便程序员的使用,苹果提供了全局队列
- 在使用多线程并发时,如果对队列没有特殊需求,在执行异步任务时,可以直接使用全局队列
1.4 队列和函数
串行队列 | 并发队列 | |
---|---|---|
同步函数 | ||
异步函数 |
1.5 死锁
-
主线程的死锁
- 主线程因为同步函数的原因等着先执行任务
- 主队列等着主线程的任务执行完毕再执行自己的任务
- 主队列和主线程相互等待会造成死锁
-
死锁的条件
-
互斥条件
:一个资源每次只能被一个进程使用,即在一段时间内某资源仅为一个进程所占有。此时若有其他进程请求该资源,则请求进程只能等待。 -
请求与保持条件
:进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源已被其他进程占有,此时请求进程被阻塞,但对自己已获得的资源保持不放。 -
不可剥夺条件
:进程所获得的资源在未使用完毕之前,不能被其他进程强行夺走,即只能由获得该资源的进程自己来释放(只能是主动释放)。 -
循环等待条件
:若干进程间形成首尾相接循环等待资源的关系
-
(二)GCD的应用
2.1 dispatch_async 和 dispatch_sync
测试代码1
dispatch_queue_t queue = dispatch_queue_create("xy", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"1");
dispatch_async(queue, ^{
NSLog(@"2");
dispatch_sync(queue, ^{
NSLog(@"3");
});
NSLog(@"4");
});
NSLog(@"5");
- 输出结果1:
1(52)34
- 1,在当前线程,也就是主线程中执行,肯定是第一个;
- (52),虽然5也是主线程中,理论上要在2前面执行,但当任务非常简单时,2有可能会在5的前面执行;
- (34),3是同步执行,肯定要在4前面;
2020-07-07 23:47:10.420291+0800 001---函数与队列[3440:96357] 1
2020-07-07 23:47:10.421551+0800 001---函数与队列[3440:96357] 5
2020-07-07 23:47:10.421625+0800 001---函数与队列[3440:96598] 2
2020-07-07 23:47:10.421798+0800 001---函数与队列[3440:96598] 3
2020-07-07 23:47:10.421930+0800 001---函数与队列[3440:96598] 4
测试代码2
// 同步队列
dispatch_queue_t queue = dispatch_queue_create("xy", DISPATCH_QUEUE_SERIAL);
NSLog(@"1");
// 异步函数
dispatch_async(queue, ^{
NSLog(@"2");
NSLog(@"4");
dispatch_sync(queue, ^{
NSLog(@"3");
});
});
NSLog(@"5");
- 输出结果
1524 crash
- 因为 queue 是一个串行队列,同步等待一个串行任务是会发生死锁的
-
有一个面试的程序员解释了这个现象。面试官问:“什么是死锁”,程序员说:“你让我进公司,我就告诉你什么是死锁”
测试代码3
dispatch_queue_t queue = dispatch_queue_create("com.xy.cn", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"1");
});
dispatch_async(queue, ^{
NSLog(@"2");
});
dispatch_sync(queue, ^{
NSLog(@"3");
});
NSLog(@"0");
dispatch_async(queue, ^{
NSLog(@"7");
});
dispatch_async(queue, ^{
NSLog(@"8");
});
dispatch_async(queue, ^{
NSLog(@"9");
});
- 输出结果:(312) 0 (789),
- 312 在 0 的前面,因为第一个异步任务太短,有可能在第三个同步任务添加之前就已经完成,所以 123 顺序不确定;
- 0 一定在 3 后面,因为任务三是一个同步任务,必须要等任务三执行完成之后,才会执行后面的任务;
- 0 是在当前线程的任务,就没啥好说的
- 789 都是异步任务,顺序无法确定