iOS 多线程编程(一)多线程基础

多线程系列篇章计划内容:
iOS多线程编程(一) 多线程基础
iOS多线程编程(二) Pthread
iOS多线程编程(三) NSThread
iOS多线程编程(四) GCD
iOS多线程编程(五) GCD的底层原理
iOS多线程编程(六) NSOperation
iOS多线程编程(七) 同步机制与锁
iOS多线程编程(八) RunLoophtml

现代计算机系统中,CPU做为计算机系统的运算控制核心,是信息处理、程序运行的最终执行单元。操做系统做为计算机的管理者,负责任务的调度资源的分配和管理,协调着各个硬件(如CPU、内存,硬盘、网卡等)有序的工做着。程序员

在了解进程和线程以前,咱们不妨先从操做系统开始。shell

1、操做系统

1.1 操做系统概念

操做系统是管理计算机硬件与软件资源计算机程序。做为计算机最基本也最为重要的”软件“系统,操做系统须要处理如管理与配置内存决定系统资源的供需的优先次序控制输入设备与输出设备操做网络与管理文件系统等基本事务。操做系统也提供一个让用户与系统交互的操做界面。编程

从计算机用户的角度来讲,计算机操做系统体现为其提供的各项服务;从程序员的角度来讲,其主要是指用户登陆的界面或者接口;若是从设计人员的角度来讲,就是指各式各样模块和单元之间的联系。浏览器

1.2 操做系统发展的几个阶段

1.2.1 无操做系统阶段

最初的电脑没有操做系统,在这个阶段,计算机都是人工的去操做的,人们经过各类按钮来控制计算机,在操做的时候,每一个用户都会独占这台计算机。而且计算机的CPU是等待人工去操做的,当用户进行输入和输出的时候,内存CPU都是空闲的。markdown

所以,在没有操做系统的时代,计算机资源的利用率很低网络

1.2.2 批处理系统阶段

后来出现了汇编语言,操做人员经过有孔的纸带将程序输入电脑进行编译。 在批处理系统阶段,计算机就无须再等待人工的输入了。由于这个时候用户会批量的导入任务,这样,用户在导入任务以后就能够离开,计算机就能够自动的去工做。多线程

虽然在这个批处理系统里面,咱们能够将任务批量的输入,可是这个时候计算机只能同时运行一个任务。也就是说,虽然任务是批处理输入的,可是计算机每次仍是只能执行一个任务。并发

为了解决这一问题,在这个阶段提出了一个很是重要的概念,叫多道程序设计oop

多道程序设计

多道程序设计指的是在计算机内存中同时存放多个程序,而且这多个程序互相不干扰。 这里的多道程序在计算机的管理程序之下相互穿插运行,以此来提高计算机资源的利用率。

1.2.3 分时系统阶段

在这个阶段,最重要的设计就是人机交互的设计,在前面两个阶段,程序在执行的过程当中,人是没有办法去干预的,在分时系统阶段,人机就能够进行交互了。而且,人能够实时的去调试程序,这个分时系统容许多个用户去共享这个计算机的资源,所以在这个阶段,计算的资源利用率大幅度提高。分时系统也是如今主流的系统。

1.3 操做系统的功能与意义

操做系统主要包括如下几个方面的功能 :

① 进程管理

其工做主要是进程调度,在单用户单任务的状况下,处理器仅为一个用户的一个任务所独占, 进程管理的工做十分简单。但在多道程序或多用户的状况下,组织多个做业或任务时,就要解决处理器的调度、分配和回收等问题。

② 存储管理

分为几种功能:存储分配、存储共享、存储保护 、存储扩张。

③ 设备管理

分有如下功能:设备分配、设备传输控制 、设备独立性。

④ 文件管理

文件存储空间的管理、目录管理 、文件操做管理、文件保护。

⑤ 做业管理

负责处理用户提交的任何要求。

计算机的操做系统对于计算机能够说是十分重要的。

  • 操做系统会统一管理着计算机资源

好比咱们须要计算机计算”1+1“,并非直接告诉CPU说咱们要算"1+1",而是借助操做系统,让操做系统去告诉硬件咱们要作什么事情; 再好比咱们存储或读取一个文件的时候,咱们不是直接去控制存储器里边的机械设备读取的,而是经过操做系统去读写这些信息的。
操做系统实现了对计算机资源的抽象。这个抽象就是经过管理软件来实现的,这些管理软件屏蔽了硬件设备,而且给用户提供了逻辑设备,使得每一个用户在使用的时候都是同样的。

  • 用户无需面向硬件接口编程

计算机发展到如今,设备种类繁多复杂。操做系统提供统一的操做界面,屏蔽了不一样设备之间的差别。有了操做系统咱们就不须要关注不一样的设备,也不须要关注不一样的接口。
也就是说咱们无需面向存储器、IO设备这些硬件,而只需面向操做系统去编程就能够了。 举个例子,在操做系统里边有IO设备管理软件,这个软件提供了读写接口,用户在进行编程时,直接调用这个接口,并不须要具体的接触某个IO设备。

  • 操做系统提供了用户与计算机之间的接口

在不一样的设备上,操做系统可向用户呈现多种操做手段。如:

图形窗口形式(好比在手机上,咱们能够经过手指的触摸控制手机里的硬件设备(如摄像头、声音)、而在咱们普通的PC端,咱们主要是经过鼠标、键盘来控制硬件)。)

命令形式(Linux中经过在shell终端中输入命令的方式)

系统调用形式(主要是编程的时候,好比打开文件、读取数据这些操做,都是经过系统调用来完成的) 操做系统的简易性使得更多人可以使用计算机。更多的人可使用计算机就意味着解放和发展的生产力,对人类科技的提高是大有帮助的。

1.4 小结

在没有操做系统的时代,资源只属于当前运行的程序,且计算机只能运行一个程序,计算机资源利用率很低。

有了多道程序设计的概念,为了实现程序的共用,以及对计算机资源的管理,便有了操做系统。

2、进程与线程

也是基于多道程序设计的概念,为了使多个程序能并发执行,以提升资源的利用率和系统的吞吐量。进程也就出现了,进程的做用就是合理的隔离资源(由于有多道程序的概念,因此在计算机中就可能会有多个进程共同的使用某一个物理设备,好比存储器。那么进程在这里面就是发挥运行资源隔离的做用)、提高资源利用率

一个程序在运行过程当中会涉及不少操做,利用 CPU 计算、经过磁盘 IO 进行数据传输等等,咱们知道当程序在进行磁盘 IO 的时候,由于速度问题,会比较慢,所在在这个过程当中 CPU 会空闲下来,这会形成资源的浪费,正由于引入进程,在 A 进程进行磁盘 IO 的时候,会让出 CPU 给 B 进程,合理地利用了 CPU 资源,使得程序之间能够并发执行。

2.1 进程

进程(Process)是计算机中具备必定独立功能的程序关于某个数据集合的一次运行活动。它能够申请和拥有系统资源,是系统进行资源分配和调度的基本单位(有了多道程序的概念,操做系统就能够对每一个程序进行资源的分配)。

从狭义上,能够将进程理解为正在运行的程序的实例,当一个程序进入内存运行时,系统就会建立一个进程,并为它分配资源,而后把该进程放入进程就绪队列,进程调度器选中它的时候就会为它分配CPU时间,程序开始真正运行。

下图为Mac系统下的活动监视器

从图片来看,每个进程都占有 CPU内存能耗磁盘网络等资源。

每个进程都有它本身的地址空间,通常状况下,进程由 3 个部分组成,分别是程序代码数据集、栈进程控制块(Process Control Block)。

各自的做用以下:

  • 程序代码:描述进程要完成哪些功能以及如何完成。
  • 数据集、栈:程序在执行时所须要的数据和工做区。
  • 进程控制块(PCB):包含进程的描述信息和控制信息。用来记录进程的外部特征,描述进程的执行变化过程,系统能够利用它来控制和管理进程,它是系统感知进程存在的惟一标志。

进程具备的特征:

  • 并发性:任何进程均可以同其余进行一块儿并发执行;
  • 动态性:进程是程序的一次执行过程,是临时的,有生命期的,是动态产生,动态消亡的;
  • 独立性:进程是系统进行资源分配和调度的一个独立单位;
  • 结构性:进程由程序,数据和进程控制块三部分组成

2.2 线程

在早期的操做系统中并无线程的概念,进程是拥有资源和独立运行的最小单位。任务调度采用的是时间片轮转抢占式调度方式,而进程做为任务调度的最小单位,每一个进程有各自独立的一块内存,使得各个进程之间内存地址相互隔离。

后来,随着计算机技术的发展,可运行的进程愈来愈多。进程出现了不少弊端,一是因为进程是资源拥有者,建立、撤消与切换存在较大的时空开销,所以须要引入轻型进程;二是因为对称多处理机(SMP)的出现,能够知足多个运行单位,而多个进程并行开销过大。所以出现了能独立运行的基本单位——线程(Threads)

线程是程序执行中一个单一顺序控制流程,是程序执行流的最小单元,是处理器调度和分派的基本单位。

在引入线程的操做系统中,一般都是把进程做为分配资源的基本单位,而把线程做为独立运行和独立调度的基本单位。因为线程比进程更小,基本上不拥有系统资源,故对它的调度所付出的开销就会小得多,能更高效的提升系统内多个程序间并发执行的程度,从而显著提升系统资源的利用率和吞吐量

2.3 进程与线程的关系:

一个正在运行的软件(如迅雷)就是一个进程,一个进程能够同时运行多个任务( 迅雷软件能够同时下载多个文件,每一个下载任务就是一个线程)。他们的关系以下:

  • 线程是依附于进程的,不能独立存在,它包含在进程之中,是进程中的实际运做单位。进程一旦结束,全部线程都结束。

  • 一个线程只能属于一个进程,而一个进程能够有多个线程,但至少有一个线程。

  • 线程是进程中的一个执行单元,由CPU独立调度执行,负责当前进程中任务的执行。一个进程能够有一个或多个线程,线程会拥有本身的堆栈局部变量(不共享),可是它与同一进程中的多个线程将共享程序的内存空间,也就是该进程中的代码段(代码和常量),数据段(全局变量和静态变量),扩展段(堆存储)等系统资源。

引用一个例子:

若是把上课的过程比做进程,把老师比做CPU,那么能够把每一个学生比做每一个线程,全部学生共享这个教室(也就是全部线程共享进程的资源),上课时学生A向老师提出问题,老师对A进行解答,此时可能会有学生B对老师的解答不懂会提出B的疑问(注意:此时可能老师尚未对A同窗的问题解答完毕),此时老师又向学生B解惑,解释完以后又继续回答学生A的问题,同一时刻老师只能向一个学生回答问题(即:当多个线程在运行时,同一个CPU在某一个时刻只能服务于一个线程,可能一个线程分配一点时间,时间到了就轮到其它线程执行了,这样多个线程在来回的切换)

2.4 进程和线程的区别:

  • ①. 根本区别:进程是操做系统分配资源的最小单位,线程程序执行的最小单位。

全部与该进程有关的资源,都被记录在进程控制块(PCB)中。以表示该进程拥有这些资源或正在使用它们。进程也是抢占处理机的调度单位,它拥有一个完整的虚拟地址空间。当进程发生调度时,不一样的进程拥有不一样的虚拟地址空间,而同一进程内的不一样线程共享同一地址空间。

与进程相对应,线程与资源分配无关,它属于某一个进程,并与进程内的其余线程一块儿共享进程的资源。 一个标准的线程由线程ID、当前指令指针PC、寄存器、堆栈线程控制表TCB组成。 而进程由内存空间(代码,数据,进程空间,打开的文件)和一个或多个线程组成。

  • ②. 地址空间和其它资源: 进程之间相互独立,但同一进程下的各线程共享程序的内存空间及一些进程级的资源。某进程内的线程在其余进程不可见。

  • ③. 调度和切换:线程上下文切换比进程上下文切换要快得多。

  • ④. 通讯机制:进程间通讯,因为它们具备独立的数据空间,须要进程同步和互斥手段的辅助,以保证数据的一致性,方式不只耗时,并且不方便;但同一进程下的线程之间数据共享,能够直接读写进程数据段(如全局变量)来进行通讯;

  • ⑤. 内存分配:系统在运行的时候会为每一个进程分配不一样的内存空间,而线程除了CPU外,不会再分配空间,而是使用进程的资源空间。

2.5 举个例子:

3、多进程与多线程

现代操做系统,好比Mac OSUNIXLinuxWindows等,都是支持“多任务”的操做系统。

也就是操做系统能够同时运行多个任务。好比,你能够一边用浏览器上网,一边听音乐,一边写代码,对于操做系统而言,这就是多任务。

在早期单核CPU时代尚未线程的概念,只有进程。因为CPU执行代码都是顺序执行的。那么,单核CPU是怎么执行多任务的呢?

答案就是时间片轮转调度:简单地说就是把一个处理器划分为若干个短的时间片,每一个进程会被操做系统分配一个时间片(即每次被 CPU 选中来执行当前进程所用的时间),每一个时间片依次轮流地执行处理各个应用程序,时间一到,不管进程是否运行结束,操做系统都会强制将 CPU 这个资源转到另外一个进程去执行,因为一个时间片很短,相对于一个应用程序来讲,就好像是处理器在为本身单独服务同样,从而达到多个应用程序在同时进行的效果。

如上图所示,每个小方格表明一个时间片,大约100ms。假设如今我同时开着 Word、QQ、网易云音乐三个软件,CPU 首先去处理 Word 进程,100ms时间一到,CPU 就会被强制切换到 QQ 进程,处理100ms后又切换到网易云音乐进程上,100ms后又去处理 Word 进程,如此往复不断地切换。表面上看,每一个任务都是交替执行的,可是,因为CPU的执行速度实在是太快了,咱们感受就像全部任务都在同时执行同样。

随着运行的进程愈来愈多,人们发现进程的建立、撤销与切换存在着较大的时空开销,所以业界急需一种轻型的进程技术来减小开销。因而上世纪80年代出现了一种叫 SMP(Symmetrical Multi-Processing)的对称多处理技术,就是咱们所知的线程概念。线程切换的开销要小不少,这是由于每一个进程都有属于本身的一个完整虚拟地址空间,而线程隶属于某一个进程,与进程内的其余线程一块儿共享这片地址空间,基本上就能够利用进程所拥有的资源而无需调用新的资源,故对它的调度所付出的开销就会小不少。

以 QQ 聊天软件为例,上文咱们一直都在说不一样进程如何流畅的运行,此刻咱们只关注一个进程的运行状况。若是没有线程技术的出现,当 QQ 这个进程被 CPU “临幸”时,我是该处理聊天呢仍是处理界面刷新呢?若是只处理聊天,那么界面就不会刷新,看起来就是界面卡死了。有了线程技术后,每次 CPU 执行100ms,其中30ms用于处理聊天,40ms用于处理传文件,剩余的30ms用于处理界面刷新,这样就可使得各个组件能够“并行”的运行了。

自此以后,多线程技术获得普遍应用。

3.1 多进程 vs 多线程

多任务既能够由多进程实现,也能够由单进程内的多线程实现,还能够混合多进程+多线程。混合多进程和多线程的程序涉及到同步、数据共享的问题,这种模型更复杂,实际不多采用。

和多进程相比,多线程的优点在于:

  • 线程的调度与切换比进程不少,同时建立一个线程的开销也比进程要小不少;
  • 线程之间的通讯更方便,同一进程下的线程共享全局变量、静态变量等数据,线程间通讯就是读写同一个变量,速度很快。而进程之间的通讯须要以通讯的方式(Inter Process Communication,IPC)进行。

多进程的优势在于:

  • 多进程程序更健壮,在多进程的状况下,一个进程崩溃不会影响其余进程,而在多线程的状况下,任何一个线程崩溃会直接致使整个进程崩溃。

3.2 多线程的意义:

多线程最大的意义,就是最大限度地利用CPU资源。

  • 某个操做可能会陷入长时间等待,等待的线程会进入睡眠状态,没法继续执行。多线程执行能够有效利用等待时间进行线程切换。如等待网络响应。
  • 某个操做可能会消耗大量的时间,若是只有一个线程,程序和用户之间的交互会被中断。多线程可让一个线程负责交互另外一个线程负责计算
  • 程序自己要求并发操做,如一个多端下载软件(如Bittorrent)。
  • 多CPU或多核处理器,自己具有同时执行多个线程的能力,所以单线程程序没法全面发挥计算机的所有计算能力。
  • 多线程能够提升程序的效率。多线程同步完成多项任务,不是为了提升运行效率,而是为了提升资源使用效率来提升系统的效率。

固然,多线程也并非百利而无害的。

  • 开启线程须要占用必定的内存空间(默认状况下,每条线程占512kb);若是开启大量的线程,会占用大量的内存空间,下降程序的性能。
  • 线程越多,CPU在调用线程上的开销就越大(由于要在线程之间切换);
  • 多线程编程的程序设计会更加复杂(如线程间的通讯、多线程的数据共享等)。使用不当,可能会带来更多的bug

3.3 并行与并发:

并行(parallel):指在同一时刻,有多条指令在多个处理器上同时执行;

并发(concurrency):指在同一时刻只能有一条指令执行,但多个进程指令被快速的轮换执行,使得在宏观上具备多个进程同时执行的效果,但在微观上并非同时执行的,只是把时间分红若干段,使多个进程快速交替的执行

真正的并行执行多任务只能在多核CPU上实现,可是,因为任务数量远远多于CPU的核心数量,因此,操做系统也会自动把不少任务轮流调度到每一个核心上执行。

4、 线程的状态(生命周期)

4.1 新建态(new)

建立一个Thread对象就生成一个新线程。建立完成后就须要为线程分配内存。当线程处于"新线程"状态时,仅仅是一个空线程对象,它尚未分配到系统资源。所以只能启动或终止它。任何其余操做都会引起异常。

例如,一个线程调用了new方法,并在调用start方法以前的就处于新线程状态,能够调用startstop方法。

4.2 就绪态(Runnable)

一个新建立的线程并不自动开始运行,要执行线程,必须调用线程的start()方法。当线程对象调用start()方法即启动了线程,建立线程运行所必须的系统资源,并调度线程执行run()方法 ,当start()方法返回后,线程就处于就绪状态

处于就绪状态的线程并不必定当即运行run()方法,线程还必须同其余线程竞争CPU时间,只有得到CPU时间才能够运行线程。由于在单CPU的计算机系统中,不可能同时运行多个线程,一个时刻仅有一个线程处于运行状态。所以,此时可能有多个线程处于就绪状态。

4.3 运行态(Running)

当线程得到CPU时间后,它才进入运行状态,真正开始执行run()方法。

4.4 阻塞态(Blocked)

所谓阻塞状态是正在运行的线程没有运行结束,暂时让出CPU,这时其余处于就绪状态的线程就能够得到CPU时间,进入运行状态。

将线程挂起(sleep()、wait()、join()、没有获取到锁都会使线程阻塞),可能将资源交给其它线程使用。

suspend()方法被调用。

sleep()方法被调用。

③ 线程使用wait()来等待条件变量。

④ 线程处于I/O请求的等待。

⑤ 线程试图获得一个,而该锁正被其余线程持有。

4.5 死亡态(Dead)

有两个缘由会致使线程死亡:

run()方法正常退出而天然死亡;

② 发生异常或者被打断interrupt()致使线程终止。

run()方法返回,或别的线程调用stop()方法,线程进入死亡态。一般Apple使用它的stop()方法来终止它产生的全部线程。

拓展阅读:

进程和线程

进程知多少?

相关文章
相关标签/搜索