关于多线程
- 概念:
- 同一个进程中同时开启多个线程,每条线程执行不同的任务。
- 本质:速度快
- 同一时间,CPU只能处理一条线程,意味着只有一条线程在执行。
- CPU以人类难以察觉的速度在不同的线程之间切换,造成多条线程并发执行的假象。
- 如果线程非常非常多,CPU切换频繁,消耗大量资源,线程执行效率降低。
- 优点:
- 多条线程同时(并发)执行,提高程序的执行效率。
- 提高资源利用率,包括CPU、内存等。
- 缺点
- 开启新线程会占用一定内存,线程过多会降低性能。
- 2.程序设计更加复杂,比如线程之间的通信、多条线程的数据共享。
-
附:本文目录
iOS多线程.png
一、基本概念
- 进程
- 可以理解成一个运行中的应用程序,是系统进行资源分配和调度的基本单位,是操作系统结构的基础,主要管理资源。
- 每个进程之间是独立的,每个进程均运行在其专用且受保护的内存空间内。
- 线程
- 线程是进程的基本执行单元,即进程想要执行任务,必须得有线程。
- 程序执行流的最小单元,线程是进程中的一个实体。
- 同步线程
- 在当前线程内按照创建的先后顺序依次执行,不开启新线程。
- 异步线程
- 在当前线程内开启多个新的线程,同时运行,无先后关系。
- 队列
- 装载线程任务的队形结构。
- 并发
- 线程执行可以同时一起进行执行。
- 串行
- 线程执行只能依次逐一先后有序的执行。
- 线程通讯
- 在一个进程中,通常有多个线程,线程不是孤立存在的,线程之前需要"沟通交流"。
注意点
- 一个进程可有多个线程。
- 一个进程可有多个队列。
- 队列可分并发队列和串行队列。
二、创建方式
- NSThread
- 每个NSThread对象对应一个线程,真正最原始的线程。
- 优点
- NSThread 轻量级最低,相对简单。
- 可直接操作线程对象。
- 缺点
- 手动管理所有的线程活动,如生命周期、线程同步、睡眠等。
- NSOperation
- 自带线程管理的抽象类。
- 优点
- 自带线程周期管理,操作上可更注重自己逻辑。
- 缺点
- 面向对象的抽象类,只能实现它或者使用它定义好的两个子类:NSInvocationOperation 和 NSBlockOperation。
- GCD
*Grand Central Dispatch是由苹果开发的一个多核编程的解决方案。iOS4.0+才能使用。
- 优点
- 最高效,避开并发陷阱。
- 苹果官方推荐使用。
- 使用比上面2种更为简单。
- 性能更好,GCD自动根据系统负载来增减线程数量,这就减少了上下文切换以及增加了计算效率。
- 开发者只需要告诉 GCD 想要如何执行什么任务,不需要编写任何线程管理代码。
- 缺点
- 基于C实现。
三、如何选择
- 简单而安全的选择NSOperation实现多线程即可。
- 处理大量并发数据,又追求性能效率的选择GCD。
- NSThread本人选择基本上是在做些小测试上使用。
- 个人推荐使用gcd。
四、生命周期
-
无论使用哪种方式创建的线程,在正常情况下它的生命周期都是一样的。
状态示意图.png
-
概念
新建:实例化线程对象
- 就绪:向线程对象发送start消息,线程对象被加入可调度线程池等待CPU调度。
- 运行:CPU 负责调度可调度线程池中线程的执行。线程执行完成之前,状> * 态可能会在就绪和运行之间来回切换。就绪和运行之间的状态变化由CPU负> * 责,程序员不能干预。
- 阻塞:当满足某个预定条件时,可以使用休眠或锁,阻塞线程执行。sleepForTimeInterval(休眠指定时长),sleepUntilDate(休眠到指定日期),@synchronized(self):(互斥锁)。
- 死亡:正常死亡,线程执行完毕。非正常死亡,当满足某个条件后,在线程> * 内部中止执行/在主线程中止线程对象
- 还有线程的exit和cancel
- [NSThread exit]:一旦强行终止线程,后续的所有代码都不会被执行。
- [thread cancel]取消:并不会直接取消线程,只是给线程对象添加 isCancelled 标记。
五、使用方法(详细介绍GCD的使用方法)
- NSThread 创建线程
//动态创建线程
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(threadAction:) object:@"one"];
thread.threadPriority = 1;
[thread start];
//静态创建线程
[NSThread detachNewThreadSelector:@selector(threadAction:) toTarget:self withObject:@"two"];
//隐式创建线程
[self performSelectorInBackground:@selector(threadAction:) withObject:@"three"];
//响应方法
-(void)threadAction:(id)object
{
NSLog(@"object = %@",object);
}
- 线程通讯
//在指定线程上执行操作
[self performSelector:@selector(threadAction:) onThread:thread withObject:nil waitUntilDone:YES];
//在主线程上执行操作
[self performSelectorOnMainThread:@selector(threadAction:) withObject:nil waitUntilDone:YES];
//在当前线程执行操作
[self performSelector:@selector(threadAction:) withObject:nil];
- NSOperation
//NSInvocationOperation创建线程。
NSInvocationOperation *invocationOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(threadAction:) object:@"six"];
//[invocationOperation start];//直接会在当前线程主线程执行
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
[queue addOperation:invocationOperation];
GCD
1.任务和队列
- 1.1 任务
- 就是执行操作的意思,换句话说就是你在线程中执行的那段代码。在 GCD 中是放在 block 中的。
- 执行任务有两种方式:同步执行(sync)和异步执行(async)。两者的主要区别是:是否等待队列的任务执行结束,以及是否具备开启新线程的能力。
同步执行(sync):
* 同步添加任务到指定的队列中,在添加的任务执行结束之前,会一直等待,直到队列里面的任务完成之后再继续执行。
* 只能在当前线程中执行任务,不具备开启新线程的能力。
异步执行(async):
* 异步添加任务到指定的队列中,它不会做任何等待,可以继续执行任务。
* 可以在新的线程中执行任务,具备开启新线程的能力。
1.2 队列
- 这里的队列指执行任务的等待队列,即用来存放任务的队列。队列是一种特殊的线性表,采用 FIFO(先进先出)的原则,即新任务总是被插入到队列的末尾,而读取任务的时候总是从队列的头部开始读取。每读取一个任务,则从队列中释放一个任务。
- 在 GCD 中有两种队列:串行队列和并发队列。两者都符合 FIFO(先进先出)的原则。两者的主要区别是:执行顺序不同,以及开启线程数不同。
串行队列(Serial Dispatch Queue):
* 每次只有一个任务被执行。让任务一个接着一个地执行。(只开启一个线程,一个任务执行完毕后,再执行下一个任务)
* 对于串行队列,GCD 提供了的一种特殊的串行队列:主队列(Main Dispatch Queue),所有放在主队列中的任务,都会放到主线程中执行。
并发队列(Concurrent Dispatch Queue):
* 可以让多个任务并发(同时)执行。(可以开启多个线程,并且同时执行任务)
注意:并发队列的并发功能只有在异步(dispatch_async)函数下才有效
* 对于并发队列,GCD 默认提供了全局并发队列(Global Dispatch Queue)。
2. GCD使用步骤
- 创建一个队列(串行队列或并发队列)
- 将任务追加到任务的等待队列中,然后系统就会根据任务类型执行任务(同步执行或异步执行)
3. 队列的创建
- 可以使用dispatch_queue_create来创建队列,需要传入两个参数,第一个参数表示队列的唯一标识符,用于 DEBUG,可为空,Dispatch Queue 的名称推荐使用应用程序 ID 这种逆序全程域名;第二个参数用来识别是串行队列还是并发队列。DISPATCH_QUEUE_SERIAL 表示串行队列,DISPATCH_QUEUE_CONCURRENT 表示并发队列。
// 串行队列的创建方法
dispatch_queue_t queue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_SERIAL);
// 并发队列的创建方法
dispatch_queue_t queue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_CONCURRENT);
4. 任务的创建
- GCD 提供了同步执行任务的创建方法dispatch_sync和异步执行任务创建方法dispatch_async。
// 同步执行任务创建方法
dispatch_sync(queue, ^{
// 这里放同步执行任务代码
});
// 异步执行任务创建方法
dispatch_async(queue, ^{
// 这里放异步执行任务代码
});
5. 基本使用
![](https://img.haomeiwen.com/i11866212/6575de6745f558d0.png)
-
同步执行 + 并发队列
同步执行 + 并发队列.png
从上图可以看出:
- 所有任务都是在当前线程(主线程)中执行的,没有开启新的线程(同步执行不具备开启新线程的能力)。
- 所有任务都在打印的syncConcurrent---begin和syncConcurrent---end之间执行的(同步任务需要等待队列的任务执行结束)。
- 任务按顺序执行的。按顺序执行的原因:虽然并发队列可以开启多个线程,并且同时执行多个任务。但是因为本身不能创建新线程,只有当前线程这一个线程(同步任务不具备开启新线程的能力),所以也就不存在并发。而且当前线程只有等待当前队列中正在执行的任务执行完毕之后,才能继续接着执行下面的操作(同步任务需要等待队列的任务执行结束)。所以任务只能一个接一个按顺序执行,不能同时被执行。
- 特点:在当前线程中执行任务,不会开启新线程,执行完一个任务,再执行下一个任务。
-
异步执行 + 并发队列
异步执行 + 并发队列.png
从上图可以看出:
- 除了当前线程(主线程),系统又开启了3个线程,并且任务是交替/同时执行的。(异步执行具备开启新线程的能力。且并发队列可开启多个线程,同时执行多个任务)。
- 所有任务是在打印的syncConcurrent---begin和syncConcurrent---end之后才执行的。说明当前线程没有等待,而是直接开启了新线程,在新线程中执行任务(异步执行不做等待,可以继续执行任务)。
- 特点:可以开启多个线程,任务交替(同时)执行。
-
同步执行 + 串行队列
同步执行 + 串行队列.png
从上图可以看出:
- 所有任务都是在当前线程(主线程)中执行的,并没有开启新的线程(同步执行不具备开启新线程的能力)。
- 所有任务都在打印的syncConcurrent---begin和syncConcurrent---end之间执行(同步任务需要等待队列的任务执行结束)。
- 任务是按顺序执行的(串行队列每次只有一个任务被执行,任务一个接一个按顺序执行)。
- 特点:不会开启新线程,在当前线程执行任务。任务是串行的,执行完一个任务,再执行下一个任务。
-
异步执行 + 串行队列
异步执行 + 串行队列.png
从上图可以看出:
- 开启了一条新线程(异步执行具备开启新线程的能力,串行队列只开启一个线程)。
- 所有任务是在打印的syncConcurrent---begin和syncConcurrent---end之后才开始执行的(异步执行不会做任何等待,可以继续执行任务)。
- 任务是按顺序执行的(串行队列每次只有一个任务被执行,任务一个接一个按顺序执行)。
- 特点:会开启一条新线程,但是因为任务是串行的,执行完一个任务,再执行下一个任务。
结尾
- 以上纯属个人观点,如有不对欢迎指出。
网友评论