【iOS】多线程基础
文章目录
- 【iOS】多线程基础
- 前言
- 进程与线程
- 进程
- 进程的状态
- 进程的一个控制结构
- 进程的上下文切换
- 线程
- 为什么要用线程
- 什么是线程
- 线程和进程的关系
- 线程的上下文切换
- 线程和进程的优缺点
- 小结
前言
笔者由于对于GCD不是很了解,导致了项目中网络请求哪部分的一个代码冗长且逻辑混乱,很多时候出现了问题也不知道怎么解决,所以笔者决定学习一下多线程的内容,方便自己之后对于GCD的一些内容的理解。
进程与线程
对于任何一个学习计算机的人而言,对于进程和线程这两个词语一定不陌生,但是要具体说出二者的区别,可能还是会支支吾吾说不出个所以然,这里笔者简单介绍一下有关进程和线程的内容。
进程
我们编写的代码都只是一个存储在计算机硬盘的一个静态文件,通过编译后会变成一个二进制可执行文件,当我们运行这个可执行文件后,他会进入内存中,然后我们计算机的cpu会开始执行这个程序的每一条指令,这时候这个运行中的程序就被称作进程, 也就是这个运行的程序实例被叫做进程。
用官方一点的话来讲就是:进程(Process)是计算机中具有一定独立功能
的程序关于某个数据集合的一次运行活动。 它可以申请和拥有系统资源,是系统进行资源分配和调度
的基本单位(有了多道程序的概念,操作系统就可以对每个程序进行资源的分配)。
这时候,我们的计算机如果执行一个读取硬盘文件的数据内容被执行了,当运行执行读取文件的指令的时候,cpu开始从硬盘读取数据,这里我们不难发现一个问题,我们计算机读取硬盘的顺序是非常缓慢的,如果我们一直让计算机等待这个文件执行完然后在执行下面的内容的话,我们会浪费很多时间,cpu的利用率是非常的低下的。
举个例子,就好比我们吃饭的时候我们一直等待厨师把饭做好,在这期间我们什么事情也不做,就单纯的等待吃饭,这时候我们一般会去抽时间做不同的事情,然后等厨师做好饭就回来吃。所以现在的计算机也采用了这种思想,如果在执行一个进程中读取文件的内容的时候,cpu会切换到另一个进程中去执行另一个进程的相关内容,当硬盘的数据返回的时候,cpu会收到一个中断,cpu在回来执行这个进程的内容。
这种 多个程序,交替执行的思想,就是cpu管理多个进程的初步想法。虽然一个单核的cpu在某一刻只能执行一个进程,但是在一个时间段内却可以执行多个进程,这样就会给我们产生一种并行的错觉,好像这几个进程是一起执行的,但是实际上这种方式叫做并发
现在的主流操作系统都是支持”多任务“的操作系统。
举个例子:也就是操作系统可以同时运行多个任务。比如,你可以一边用浏览器上网,一边听音乐,一边写代码,对于操作系统而言,这就是多任务。
这种并发的效果是通过什么实现的呢?这里笔者引用一段博客:
答案就是时间片轮转调度:简单地说就是把一个处理器划分为若干个短的时间片,每个进程会被操作系统分配一个时间片(即每次被 CPU 选中来执行当前进程所用的时间),每个时间片依次轮流地执行处理各个应用程序,时间一到,无论进程是否运行结束,操作系统都会强制将 CPU 这个资源转到另一个进程去执行,由于一个时间片很短,相对于一个应用程序来说,就好像是处理器在为自己单独服务一样,从而达到多个应用程序在同时进行的效果。通俗的说就是讲时间分为一个个极短的时间段,在相应的时间短中执行相应的程序,因为时间段的时间极短,在我们和程序看来就好像是CPU同时处理多个进程一样。
【iOS】—— 多线程编程八重曲之(一)- 多线程基础
正如同上图每一个方块都是一个时间片,我们的计算机会给这三个程序分配对应的时间片,先运行一个程序,在这个时间片用完之后迅速切换到另一个进程中,再次执行对应的一个时间片,就这样循环往复从而实现了一个并发的效果(原因是时间片非常的短暂,人察觉不出来他的区别)。
进程的状态
在上面的多进程的例子中,我们发现进程大致有着 运行—暂停—运行的一个活动规律,就一般情况来说,一个进程不是自始自终连续不停的运行的,他与并发执行中的其他进程的执行是互相制约的。
所以一个进程在活动期间主要具备了三个基本状态: 运行状态, 就绪状态, 阻塞状态
- 运行:进程占用cpu
- 就绪:可运行,但是因为某些原因停止运行
- 阻塞:该进程正在等待某一事件的发生而停止运行
自然还会有另外两个状态,创建和结束的状态
这里笔者直接引用一段别的大佬对于这个状态变迁的描述
图片来自:进程管理
其实进程还有被挂起的一个状态,笔者这里还不是很能理解相关内容,这里涉及到了一个虚拟内存的内容,笔者会在之后了解相关内容。
进程的一个控制结构
在操作系统中是采用 **程序控制块(PCB)**这个数据结构来描述进程的。PCB是一个进程存在的唯一标识,这里笔者可以结合mac的一个活动监视器来解释相关内容。
他包含以下内容:
进程描述内容:
- 进程标识符,也就是上图中的PID
- 用户标识符,就是上图中用户对应的内容。
进程控制和管理信息
- 进程的一个状态,如new,ready
- 进程优先级,进程抢占cpu的优先级
资源分配清单
- 有关内存地址空间的信息
cpu相关信息
- CPU各种寄存器的值,以便进程被重新执行后,都能从断点处继续执行。
通过PCB我们就可以实现一个多进程的一个并发。而PCB与PCB之间是用链表来组织的,将就绪状态的进程链接在一起,就是就绪队列。将等待状态的进程链接在一起叫做阻塞队列。
进程的上下文切换
前面我们提到进程间的切换,这个从一个进程切换到另一个进程中运行,被称为上下文切换。
我们也提到了现在的操作系统都是多任务的,所以在每个任务运行钱需要知道任务从哪里进行一个加载,从哪里开始运行。
这个时候,我们就啊哟先帮cpu设置好寄存器和程序计数器。
前者是一块运行速度极快的内存,后者是一个cpu将要执行指令的位置,或者即将执行的下一条指令的位置。这两个部分是CPU执行任何任务前,必须依赖的环境,这个环境就叫做CPU上下文。
所以CPU上下文切换接可以理解为:
CPU 上下文切换就是先把前一个任务的CPU上下文(CPU 寄存器和程序计数器)保存起来,然后加载新任务的上下文到这些寄存器和程序计数器,最后再跳转到程序计数器所指的新位置,运行新任务。
cpu的上下文切换分成:进程上下文, 线程上下文, 中断上下文
进程的上下文主要包含了:虚拟内存, 栈, 全句变量等用户需要的资源,还包含了寄存器和内核堆栈的等内核空间的资源。
线程
为什么要用线程
这里笔者直接引用一段话:
在早期的操作系统中并没有线程的概念,进程是拥有资源和独立运行的最小单位。任务调度采用的是
时间片轮转
的抢占式调度
方式,而进程作为任务调度的最小单位,每个进程有各自独立的一块内存,使得各个进程之间内存地址相互隔离。后来,随着计算机技术的发展,可运行的进程越来越多。进程出现了很多弊端,一是由于进程是资源拥有者,创建、撤消与切换存在较大的时空开销,因此需要引入轻型进程;二是由于对称多处理机(SMP)的出现,可以满足多个运行单位,而多个进程并行开销过大。因此出现了能独立运行的基本单位 —— 线程(Threads)。【iOS】—— 多线程编程八重曲之(一)- 多线程基础
其实线程的出现,主要还是为了减少上下文切换的时候浪费的系统开销,线程之间可以并发运行且共享相同的地址空间,从而减小了进程上下文切换的开销,让程序运行更加流畅。
什么是线程
线程是程序执行中一个单一
的顺序控制
流程,是程序执行流的最小单元,是处理器调度和分派
的基本单位。
同一进程中内多个线程可以共享代码段,数据段,打开的文件等资源。但是每个线程由自己独立的寄存器和栈,可以保证线程的控制流是独立的
优点
- 一个进程中可以有多个线程
- 各个线程中可以并发的执行
- 各个线程之间可以共享地址内存和文件等资源
缺点
- 某一进程中的一个线程崩溃可能导致该进程内其他线程的的崩溃。
线程和进程的关系
- 线程是依附于进程的,不能独立存在,它包含在进程之中,是进程中的实际运作单位。进程一旦结束,所有线程都结束。
- 一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程。
- 线程是进程中的一个执行单元,由CPU独立调度执行,负责当前进程中任务的执行。一个进程可以有一个或多个线程,线程会拥有自己的堆栈和局部变量(不共享),但是它与同一进程中的多个线程将共享程序的内存空间,也就是该进程中的代码段(代码和常量),数据段(全局变量和静态变量),扩展段(堆存储)等系统资源。
线程的上下文切换
线程和进程最大的区别在于:线程是调度的基本单位,而进程则是资源拥有的基本单位
- 在一个进程中如果只有一个线程,那么可以吧这个进程当作一个线程
- 当进程拥有多个线程是,这些线程会共享相同的虚拟内存和全局变量等资源,这些资源在上下文切换的时候是不需要修改的。
那我们线程的上下文切换是需要保存那些内容呢?
-
如果在同一进程下
那么切换的时候,只用切换线程的私有数据,比方说寄存器等不共享的数据
-
如果不在同一进程下
那么他的上下文切换和进程上下文一样
所以线程的上下文切换相比进程,开销要小很多,所以我们有了多线程的优点
线程和进程的优缺点
多任务既可以由多进程实现,也可以由单进程内的多线程实现,还可以混合多进程+多线程。混合多进程和多线程的程序涉及到同步、数据共享的问题,这种模型更复杂,实际很少采用。
和多进程相比,多线程的优势在于:
- 线程的调度与切换比进程快很多,同时创建一个线程的开销也比进程要小很多;
线程之间的通信更方便,同一进程下的线程共享全局变量、静态变量等数据,线程间通信就是读写同一个变量,速度很快。而进程之间的通信需要以通信的方式(Inter Process Communication,IPC)进行。
而多进程的优点在于:
- 多进程程序更健壮,在多进程的情况下,一个进程崩溃不会影响其他进程,而在多线程的情况下,任何一个线程崩溃会直接导致整个进程崩溃。
小结
笔者这篇文章简单介绍了有关线程和进程的内容,笔者才疏学浅,如有纰漏还请不吝指出。以及笔者对于调度的内容还没有进行一个学习,之后学习会补充上去,接下来才可以方便我们理解iOS中GCD各个函数的一个意义。
参考博客:
【iOS】—— 多线程编程八重曲之(一)- 多线程基础
进程管理