一,进程和线程
1,进程
定义:
- 进程是指在系统中正在运行的一个应用程序
- 每个进程之间是独立的,每个进程均运行在其专有的且受保护的内存
- 进程是系统进行资源分配和调度的一个独立单位
补充:iOS系统是相对封闭的系统,App在各自的沙盒中运行,每个APP都只能读取系统为该应用程序创建的文件夹AppData下的内容,不能随意跨域自己的沙盒去访问别的App沙盒中的内容。也就是说iOS是单线程的,一个App就是一个进程。
特点:
- 独立性:是系统独立存在的实体,拥有自己独立的资源,有自己私有的地址空间。在没有经过进程本身的允许的情况下,一个用户的进程不可以直接访问其他进程的地址空间。
- 动态性:程序是一个静态的指令集合,而进程是一个正在系统中活动的指令集合,进程中加入了时间的概念。进程具有自己的生命周期和不同的状态,这些都是程序不具备的
- 并发性:多线程可以在单个处理器上并发执行,多个进程之间不会相互影响
2,线程
定义:
- 线程是进程的基本执行单位,一个进程的所有任务都在线程中执行
- 进程要想执行任务,至少要有一个线程
- 程序启动会默认开启一条线程,这条线程被称为主线程和UI线程
3,进程和线程的关系
- 地址空间:同一进程的线程共享本进程的地址空间,而进程之间则是独立的地址空间。
- 资源拥有:同一进程内的线程共享本进程的资源如内存,I/O,CPU等,但是进程之间的资源是独立的。
- 进程切换时,消耗的资源大,效率高。所以涉及到频繁的切换时,使用线程要好于进程。同样如果要求同时进行并且又要共享某些变量的并发操作,只能用线程不能用进程
- 执行过程:每个独立的进程有一个程序运行的入口、顺序执行序列和程序入口。但是线程不能独立执行,必须依存在应用程序中,由程序提供多个线程执行控制。
- 线程是处理器调度的基本单位,但是进程不是
4,任务
- 通俗的说任务就是一件事情或一段代码,线程其实就是去执行这个时期
5,队列
- 队列是先进先出的线性表。装载线程任务的队形结构。队列只允许新数据在后端进行添加。
队列的类型
- 并发队列:线程执行可以同时一起执行,不需要上一个执行完,才能执行下一个的
- 串行队列:线程执行只能依次逐一先后有序的执行,等待上一个执行完,再执行下一个
- 主队列:绑定主线程,所有任务都在主线程执行,有经过特殊处理的串行队列
- 全局队列:系统提供的并发队列
6,同步和异步
- 同步sync:只能在当前线程按先后顺序依次执行任务,不具备开启新线程的能力
- 异步async:在新的线程执行任务,具备开启新线程的能力
二,多进程和多线程
- 在早期单核CPU
如何理解多进程?在早期单核CPU时代,由于CPU执行代码都是顺序执行的,那么单核CPU如何执行多任务?
比如说一边用浏览器上网,一边听音乐,一边写代码
这里用到时间片轮换调度
:简单就是把一个处理器划分为若干个段的时间片,每个进程会被操作系统分配一个时间片(即每次被CPU选中来执行当前进程所用的时间),每个时间片依次轮流地执行处理各个应用程序,时间一到,无论进程是否运行结束,操作系统就会强制将CPU这个资源转到另一个进程去执行,由于每个时间片很短,对于每个程序,就好像为自己单独服务,从而达到多个程序同时进行的效果。
如何理解多线程?
在一个程序中,以QQ聊天为例,当QQ这个进程被CPU分配时间处理时,这时我们需要处理聊天还是处理界面的刷新。如果处理了聊天,界面就不会刷新,看起来界面就卡死了。同样上上面一样,每次 CPU 执行100ms,其中30ms用于处理聊天,40ms用于处理传文件,剩余的30ms用于处理界面刷新,这样快速切换处理。就可以达到多线程的效果。
- 在当前的多核CPU
多核CPU拥有多个物理核心,每个物理核心可以同时执行一个进程(线程)或多个进程(线程)。这意味着在多核CPU下,多个进程(线程)可以正真并行执行,每个线程在各自的核心上独立运行。
在多核CPU的情况下,也可能会出现一个CPU核心处理多个进程(线程)的情况。
每个线程在自己的核心上独立执行,互不干扰,可以充分利用CPU的并行处理能力。这种并行执行可以提高系统的响应性和吞吐量。
区别:
在多核CPU下,多线程的并发编程相对复杂,需要考虑线程间的同步、互斥和数据共享等问题。由于多个线程可以同时访问和修改共享数据,可能会出现竞态条件和数据不一致的问题,需要使用同步机制(如锁、信号量等)来实现线程安全。
而在单核CPU下,由于只有一个线程在执行,不存在多个线程同时访问共享数据的问题,因此并发编程的复杂性相对较低。
三,多线程的生命周期
- 新建:实例化线程对象
- 就绪:向线程对象发送start消息,线程对象被加入可调度线程池等待CPU调度。
- 运行:CPU负责调度可调度线程池中线程的执行,线程执行完成之前,状态可能会在就绪和运行之间来回切换。就绪和执行之间的状态变化由CPU负责。
- 阻塞:当满足某个预定条件时,可以使用休眠或锁,阻塞线程执行,所谓
阻塞状态
是正在运行的线程没有运行结束,暂时让出CPU,这时其他处于就绪状态的线程就可以获得CPU时间,进入运行状态。 - 死亡:正常死亡,线程执行完毕。非正常死亡,当满足某个条件后,在线程内部终止执行/在主线程终止线程对象
线程池
线程池是一种“池化”的线程使用模式。线程的创建,销毁,调度都有一定的开销,通过预先创建一定数量的线程,让这些线程处于就绪状态来提高系统响应速度,在线程使用完成后归还到线程池达到重复利用的目的,从而降低系统资源的消耗,提高响应速度,增加了线程的可管理性。
四,多线程的四种使用方法
我们一般使用比较多的是 GCD,因为开发者只需要告诉 GCD 想要执行什么任务,不需要编写任何线程管理代码,但这也是 GCD 的不够灵活的地方,我们无法监控线程的各个状态,这也是很多大框架中使用 NSOperation
的原因,NSOperation 相比 GCD 更加灵活,开发者可以通过 KVO
监测 Operation
的状态,自定义 NSOperation 等。
五,多线程的意义
- 某个操作可能会陷入长时间等待,等待的线程会进入睡眠状态,无法继续执行。多线程执行可以有效利用等待时间进行线程切换。如等待网络响应。
- 某个操作可能会消耗大量的时间,如果只有一个线程,程序和用户之间的交互会被中断。多线程可以让一个线程负责交互,另一个线程负责计算。
- 多CPU或多核处理器,本身具备同时执行多个线程的能力,因此单线程程序无法全面发挥计算机的全部计算能力。
- 多线程可以提高程序的效率。多线程同步完成多项任务,不是为了提高运行效率,而是为了提高资源使用效率来提高系统的效率。
多线程的不足:
- 开启线程需要占用一定的内存空间(默认情况下,每条线程占512kb);如果开启大量的线程,会占用大量的内存空间,降低程序的性能。
- 线程越多,CPU在调用线程上的开销就越大(因为要在线程之间切换);
- 多线程编程的程序设计会更加复杂(如线程间的通信、多线程的数据共享等)。