1 进程和线程
1.1 进程
进程是指在系统中正在运行的一个应用程序。
比如同时代开迅雷、Xcode、系统就会分别启动两个进程。
1.2 线程
一个进程要想执行任务,必须得有线程(每一个进程至少要有1条线程)。
一个进程的所有任务都在线程中执行。
1个线程里面的任务是串行执行的。
如果要在一个线程中执行多个任务,那么只能一个一个地按顺序执行这些任务。
也就是说,在同一个时间内,1个线程只能执行一个任务。
1.3 进程和线程的比较
线程是CPU调用的最小单位。
进程是CPU分配资源和调度的单位。
一个程序可以对应多个进程,一个进程中可以有多个线程,但至少要有一个线程。
同一个进程内的线程共享进程的资源。
2 多线程
2.1 多线程定义
1个进程可以开启多条线程,每条线程可以并行执行不同的任务。进程->车间 线程->车间工人
2.2 多线程原理
同一时间,CPU只能处理1条线程,只有1条线程在工作(单CPU)。
多线程并发执行,其实是CPU快速的在多个线程间调度(切换)。
如果CPU调度线程的时间足够快,就造成了多线程并发执行的假象。
3 多线程在ios开发中的应用
3.1
一个iOS程序运行后,默认会开启1条线程,称为主线程或者UI线程
3.2 主线程的主要作用
显示、刷新UI界面
处理UI事件(比如点击事件、滚动事件、拖动事件等)
3.3 主线程的应用注意
不要将比较耗时的操作放到主线程中
耗时操作会卡主主线程,严重影响UI的流畅度。
4 iOS开发中多线程的实现方案
技术方案 | 简介 | 语言 | 线程生命周期 | 使用频率 |
---|---|---|---|---|
pthread | 一套通用的多变成api,适用于Unix、linux、windows等系统,跨平台、可移植,使用难度大 | c | 程序管理 | 几乎不用 |
NSThread | 使用面向对象,简单易用,可以直接操作线程对象 | OC | 程序员管理 | 偶尔使用 |
GCD | 旨在替代NSThread等线程技术,充分利用设备的多核 | C | 自动管理 | 经常使用 |
NSOperation | 基于GCD(底层是GCD) | OC | 自动管理 | 经常使用 |
5 NSThread的基本使用
5.1 开启线程方法
// 需要手动启动线程
- (void)createNewThread1 {
// 1. 创建线程
/*
第一个参数:目标对象 self
第二个参数:方法选择器 调用的方法
第三个参数: 前面调用方法需要传递的参数 nil
*/
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run:) object:@"ABC"];
//2.启动线程
[thread start];
}
// 分离子线程,自动启动线程
- (void)createNewThread2 {
[NSThread detachNewThreadSelector:@selector(run:) toTarget:self withObject:@"--分离子线程--"];
}
// 开启一条后台线程
- (void)createNewThread3 {
[self performSelectorInBackground:@selector(run:) withObject:@"开启后台线程"];
}
- (void)run:(NSString *)param {
NSLog(@"---run----%@", [NSThread currentThread]);
}
6 线程的生命周期
线程的生命周期:当线程中的任务执行完毕后被释放掉。
7 线程的状态
新建线程(New)(放入可调度线程池)->就绪(Runable)->运行(Running)
001.png 002.png
8 多线程的安全隐患
8.1 资源共享问题
1块资源可能会被多个线程共享,也就是多个线程可能会访问同一块资源。
比如多个线程访问同一个对象,同一个变量,同一个文件。
当多个线程访问同一块资源时,很容易引发数据错乱和数据安全问题。
8.2 安全隐患解决-互斥锁
003.png
9 原子和非原子属性
004.png10 线程间通信
10.1 线程间通信
在一个进程中,线程间往往不是孤立的,多个线程之间需要经常及进行通信
10.2 线程间通信的体现
- 1个线程传递数据给另一个线程
- 在一个线程中执行完特定任务后,转到另一个线程继续执行任务
10.3 线程间通信常用方法
[self performSelectorOnMainThread:(nonnull SEL) withObject:(nullable id) waitUntilDone:(BOOL)]
[self performSelector:(nonnull SEL) onThread:(nonnull NSThread *) withObject:(nullable id) waitUntilDone:(BOOL)]
Snip20191210_3.png
Snip20191210_5.png
11 GCD基本概念
11.1 什么是GCD
-
全称是Grand Central Dispatch,可译为“牛逼的中枢调度器"
纯C语言,提供了非常多强大额函数 -
GCD的优势
GCD是苹果公司为多核的并行运算提供的解决方案
GCD会自动利用更多的CPU内核
GCD会自动管理线程的生命周期(创建线程、调度任务、销毁线程)
程序员只需要告诉GCD想要执行什么任务,不需要编写任何线程管理代码
11.2 任务和队列
-
GCD中有2个核心概念
任务:执行什么操作
队列:用来存放任务 -
GCD的使用就2个步骤
定制任务:确定要做的事
将任务添加到队列中
GCD会自动将队列中的任务取出,放到对应的线程中执行
任务的取出遵循队列的FIFO原则:先进先出,后进后出
11.3 执行任务
- GCD中有2个用来执行任务的常用函数
- 用同步的方式执行任务
dispatch_sync(<#dispatch_queue_t _Nonnull queue#>, <#^(void)block#>)
- 用异步的方式执行任务
dispatch_async(<#dispatch_queue_t _Nonnull queue#>, <#^(void)block#>)
- 同步和异步的区别
同步:只能在当前的线程中执行任务,不具备开启新线程的能力
异步:可以在新的线程中执行任务,具备开启新线程的能力
11.4 队列的类型
-
GCD的队列可以分为2中类型
-
并发队列(Concurrent Dispatch Queue)
可以让多个任务并发(同时)执行(自动开启多个线程同时执行任务)
并发功能只有在异步函数才有效 -
串行队列(Serial Dispatch Queue)
让任务一个接着一个地执行(一个任务执行完毕后,再执行下一个任务) -
有4个术语比较容易混淆:同步、异步、并发、串行
同步和异步的主要影响:能不能开启新的线程
同步:只能在当前的线程中执行任务,不具备开启新线程的能力
异步:可以在新的线程中执行任务,具备开启新线程的能力 -
并发和串行主要影响:任务的执行方式
并发:允许多个任务并发(同时执行)
串行:一个任务执行完毕后,再执行下一个任务
12 GCD的基本使用
// 异步函数+并发队列:会开启多线线程,队列中的任务是并发执行的
- (void)asyncConcurrent {
// 1. 创建队列
/*
第一个参数:c语言字符串,标签
第二个参数:队列类型
*/
//dispatch_queue_t queue = dispatch_queue_create("com.520it.download", DISPATCH_QUEUE_CONCURRENT);
// 获得全局并发队列
/*
第一个参数:优先级
第二个参数:备用参数
*/
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 2. 1>封装任务2>添加任务到队列中
dispatch_async(queue, ^{
NSLog(@"download1--%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"download2--%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"download3--%@",[NSThread currentThread]);
});
}
// 异步函数+串行队列:会开启1条线程,队列里面的任务是串行执行的
- (void)asyncSerial {
// 1. 创建队列
/*
第一个参数:c语言字符串,标签
第二个参数:队列类型
*/
dispatch_queue_t queue = dispatch_queue_create("com.520it.download", DISPATCH_QUEUE_SERIAL);
// 2. 1>封装任务2>添加任务到队列中
dispatch_async(queue, ^{
NSLog(@"download1--%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"download2--%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"download3--%@",[NSThread currentThread]);
});
}
// 同步函数+并发队里:不会开启线程,队列里面的任务是串行执行
- (void)syncConcurrent {
// 1. 创建队列
/*
第一个参数:c语言字符串,标签
第二个参数:队列类型
*/
dispatch_queue_t queue = dispatch_queue_create("com.520it.download", DISPATCH_QUEUE_CONCURRENT);
// 2. 1>封装任务2>添加任务到队列中
dispatch_sync(queue, ^{
NSLog(@"download1--%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"download2--%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"download3--%@",[NSThread currentThread]);
});
}
// 同步函数+串行队里:不会开启线程,队列里面的任务是串行执行
- (void)syncSerial {
// 1. 创建队列
/*
第一个参数:c语言字符串,标签
第二个参数:队列类型
*/
dispatch_queue_t queue = dispatch_queue_create("com.520it.download", DISPATCH_QUEUE_SERIAL);
// 2. 1>封装任务2>添加任务到队列中
dispatch_sync(queue, ^{
NSLog(@"download1--%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"download2--%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"download3--%@",[NSThread currentThread]);
});
}
13 主队列的相关用法
13.1 串行队列
GCD获得串行队列2中途径
- 使用dispatch_queue_create函数创建串行队列
- 使用主队列(跟主线程相关连的队列)
主队列是GCD自带的一种特殊的串行队列
放在主队列中的任务,都会放在主线程中执行
使用dispatch_get_main_queue()获得主队列
dispatch_queue_t queue = dispatch_get_main_queue();
// 异步函数+主队列:不会开启新的线程,所有任务都在主线程中串行执行
- (void)asyncMainQueue {
// 获取主队列
dispatch_queue_t queue = dispatch_get_main_queue();
// 2. 1>封装任务2>添加任务到队列中
dispatch_async(queue, ^{
NSLog(@"download1--%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"download2--%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"download3--%@",[NSThread currentThread]);
});
}
// 同步函数+主队列:死锁
- (void)syncMainQueue {
// 获取主队列
dispatch_queue_t queue = dispatch_get_main_queue();
// 2. 1>封装任务2>添加任务到队列中
dispatch_sync(queue, ^{
NSLog(@"download1--%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"download2--%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"download3--%@",[NSThread currentThread]);
});
}
注:主队列里面的任务,会放到主线程里面执行,当主线程有任务在执行的时候,主队列会等待主线程里面的任务执行完毕后,再执行主队列里面的任务
14 同步和异步函数的区别和总结
14.1 各种队列的执行效果
队列 | 并发队列 | 手动创建的串行队列 | 主队列 |
---|---|---|---|
同步(sync) | 没有开启新线程,串行执行任务 | 没有开启新线程,串行执行任务 | 死锁 |
异步(async) | 有开启新线程,并发执行任务 | 有开启新线程,任务串行执行 | 没有开启新线程,串行执行任务 |
15 线程间通信
// 创建子线程
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSURL *url = [NSURL URLWithString:@"http://hiphotos.baidu.com/bailiyu/pic/item/898e36836ca0dd9b6d81190a.jpg"];
NSData *data = [NSData dataWithContentsOfURL:url];
// 更新UI
dispatch_async(dispatch_get_main_queue(), ^{
self.imageView.image = [UIImage imageWithData:data];
});
});
16 GCD常用函数
16.1 延迟执行
// 延迟执行
- (void)delay {
// 方法1
//[self performSelector:@selector(task) withObject:nil afterDelay:2];
// 方法2
//[NSTimer scheduledTimerWithTimeInterval:2 target:self selector:@selector(task) userInfo:nil repeats:YES];
// 方法3
/*
参数1:DISPATCH_TIME_NOW 从现在开始计算时间
参数2:延迟时间 2.0 GCD单位时间:纳秒
参数3:队列
*/
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@">>>:%@",[NSThread currentThread]);
});
}
16.2 一次性代码
- (void)once {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSLog(@"%@",[NSThread currentThread]);
});
}
网友评论