.NET面试题系列[16] - 多线程概念(1)
操作系统发展史
直到20世纪50年代中期,还没出现操作系统,计算机工作采用手工操作方式。程序员将对应于程序和数据的已穿孔未的纸带(或卡片)装入输入机,然后启动输入机把程序和数据输入计算机内存,接着通过控制台开关启动程序针对数据运行;计算完毕,打印机输出计算结果;用户取走结果并卸下纸带(或卡片)后,才让下一个用户上机。
手工操作方式的两个特点:
(1)用户独占全机。不会出现因资源已被其他用户占用而等待的现象,但资源的利用率低。
(2)CPU等待手工操作。CPU的利用不充分。
20世纪50年代后期,出现人机矛盾:手工操作的慢速度和计算机的高速度之间形成了尖锐矛盾,手工操作方式已严重损害了系统资源的利用率(使资源利用率降为百分之几,甚至更低),不能容忍。唯一的解决办法:只有摆脱人的手工操作,实现作业的自动过渡。这样就出现了成批处理。
批处理系统(50年代后期)
批处理系统的追求目标是提高系统资源利用率和系统吞吐量,以及作业流程的自动化。批处理系统的一个重要缺点是不提供人机交互能力,给用户使用计算机带来不便。
批处理是指用户将一批作业提交给操作系统后就不再干预,由操作系统控制它们自动运行。这种采用批量处理作业技术的操作系统称为批处理操作系统。批处理操作系统分为单道批处理系统和多道批处理系统。批处理操作系统不具有交互性,它是为了提高CPU的利用率而提出的一种操作系统。
早期的批处理系统属于单道批处理系统,其目的是减少作业间转换时的人工操作,从而减少CPU的等待时间。它的特征是内存中只允许存放一个作业,即当前正在运行的作业才能驻留内存,作业的执行顺序是先进先出,即按顺序执行。
由于在单道批处理系统中,一个作业单独进入内存并独占系统资源,直到运行结束后下一个作业才能进入内存,当作业进行I/O操作时,CPU只能处于等待状态,因此,CPU利用率较低,尤其是对于I/O操作时间较长的作业。为了提高CPU的利用率,在单道批处理系统的基础上引入了多道程序设计技术,这就形成了多道批处理系统,即在内存中可同时存在若干道作业,作业执行的次序与进入内存的次序无严格的对应关系,因为这些作业是通过一定的作业调度算法来使用CPU的,一个作业在等待I/O处理时,CPU调度另外一个作业运行,因此CPU的利用率显著地提高了。
批处理系统中,一个作业可以长时间地占用CPU。而分时系统中,一个作业只能在一个时间片的时间内使用CPU。批处理系统不是严格意义上的操作系统。
多道程序设计与多道批处理系统(60年代中期)
多道程序技术(Multiprogramming)运行的特征:多道、宏观上并行、微观上串行。多道程序技术的提出是为了改善CPU的利用率。它需要硬件支持,并令CPU调度其他硬件工作。
(1)多道:计算机内存中同时存放几道相互独立的程序(一开始称为作业,后来演化为进程);
(2)宏观上并行:同时进入系统的几道程序都处于运行过程中,即它们先后开始了各自的运行,但都未运行完毕;
(3)微观上串行:实际上,各道程序轮流地用CPU,并交替运行。
所谓多道程序设计指的是允许多个程序同时进入一个计算机系统的主存储器并启动进行计算的方法。也就是说,计算机内存中可以同时存放多道(两个以上相互独立的)程序,它们都处于开始和结束之间。从宏观上看是并行的,多道程序都处于运行中,并且都没有运行结束;从微观上看是串行的,各道程序轮流使用CPU,交替执行。引入多道程序设计技术的根本目的是为了提高CPU的利用率,充分发挥计算机系统部件的并行性。现代计算机系统都采用了多道程序设计技术。
例如,如果一个进程有20%的时间使用CPU进行计算,另外80%的时间用来进行I/O,则在单道编程下CPU的利用率只有20%,因为在I/O时间,CPU没有事情做(只有一个进程)。
如果是支持两个进程的操作系统,即使用2道编程,则CPU只在两个进程同时进行I/O时才会处于闲置状态,因此CPU利用率将会提高到:1-0.8*0.8=0.36=>36%。同理,如果同时运行更多的进程,CPU利用率会逐步提高,直到某个临界点为止。(PS:这里的例子忽略了进程切换所需要的系统消耗)
虽然用户独占全机资源,并且直接控制程序的运行,可以随时了解程序运行情况,但这种工作方式因独占全机造成资源效率极低。于是一种新的追求目标出现了:既能保证计算机效率,又能方便用户使用计算机。20世纪60年代中期,计算机技术和软件技术的发展使这种追求成为可能。
分时操作系统
分时操作系统是使一台计算机采用时间片轮转的方式同时为几个、几十个甚至几百个用户服务的一种操作系统。
把计算机与许多终端用户连接起来,分时操作系统将系统处理机时间与内存空间按一定的时间间隔,轮流地切换给各终端用户的程序使用。由于时间间隔很短,每个用户的感觉就像他独占计算机一样。分时操作系统的特点是可有效增加资源的使用率。
分时系统是当今计算机操作系统中最普遍使用的一类操作系统。实现多任务处理的方式有抢占式(Preemption)与协作式。协作式环境下,下一个进程被调度的前提是当前进程主动放弃时间片;抢占式环境下,操作系统完全决定进程调度方案,操作系统可以剥夺耗时长的进程的时间片,提供给其它进程。
进程(Process)
每个程序最终都会加载到内存中运行。多道程序设计的出现提供了进程的雏形 – 系统内部可以存放多于一个作业。分时操作系统中,系统内部也可以同时存在超过一个程序,这些同时存在于计算机内存中的程序就被称为进程。但当我们在一个现代系统上运行一个程序时,会得到一个假象,就好像我们的程序是系统中当前运行着的唯一的程序。
进程的实现:通过操作系统的内存分配和调度。OS通过进程控制块管理进程。通过虚拟内存实现进程隔离,一个进程无法访问其他进程正在占有的资源。但不安全的操作系统例如DOS,任何进程都可以访问其他进程的资源。
进程让每个用户感觉自己在独占CPU。
进程是多道编程自然而然的产物,而多道编程的目的则是为了提高计算机CPU的利用率(或者说系统的吞吐量)。
三个视角看进程模型
(1)物理视角:从物理内存的分配来看,每个进程占用一片内存空间,从这点上看,进程其实就是内存的某片空间。由于在任意时刻,一个CPU只能执行一条指令,因此任意时刻在CPU上执行的进程只有一个,而到底执行哪条指令是由物理程序计数器指定。因此,在物理层面,所有进程共用一个程序计数器,只是CPU在不停地做进程切换。
(2)逻辑视角:从逻辑层面来看,每个进程都可以执行,也可以暂时挂起让别的进程执行,之后又可以接着执行。所以,进程需要想办法保持状态才能在下次接着执行时从正确的地点(即上下文)开始。因此,每个进程都有自己的计数器,记录其下一条指令所在的位置。(从逻辑上来说,程序计数器可以有多个)
(3)时序视角:从时间来看,每个进程都必须往前推进。在运行一定时间后,进程都应该完成了一定的工作量。换句话说,每次进程返回,它都处在上次返回点之后。
在现代操作系统中,进程管理和调度是操作系统的功能之一,特别是多任务处理的状况下,这是必要的功能。操作系统将资源分配给各个进程,让进程间可以分享与交换信息,保护每个进程拥有的资源,不会被其他进程抢走,以及使进程间能够同步(必要的话)。为了达到这些要求,操作系统为每个进程分配了一个数据结构,用来描述进程的状态,以及进程拥有的资源。操作系统可以通过这个数据结构,来控制每个进程的运作。
如何实现进程
(1)物理基础:进程的物理基础是程序,程序又运行在计算机上,因此计算机上要运行程序首先要解决进程的存储:给进程分配内存,使其安身立命。由于多个进程可能同时并存,因此需要考虑如何让多个进程共享同一个物理内存而不发生冲突。OS通过内存管理(虚拟内存和进程隔离)来解决这个问题。
(2)进程切换:进程运行实际上是指进程在CPU上执行,那么如何将CPU在多个进程之间进行切换也是一个问题。OS通过进程调度来解决这个问题。所谓进程调度,就是决定在什么时候让什么进程来使用CPU,并在调度切换进程时,不会损失之前工作的数据。
虚拟内存和进程隔离
虚拟内存是计算机系统内存管理的一种技术。它使得应用程序认为它拥有连续的可用的内存(一个连续完整的地址空间),而实际上,它通常是被分隔成多个物理内存碎片,还有部分暂时存储在外部磁盘存储器上,在需要时进行数据交换。目前,大多数操作系统都使用了虚拟内存,如Windows家族的“虚拟内存”;Linux的“交换空间”等。
电脑中所运行的程序均需经由内存执行,若执行的程序占用内存很大或很多,则会导致内存消耗殆尽。当内存耗尽时,Windows会用虚拟存储器进行补偿,它将计算机的RAM和硬盘上的临时空间组合。当RAM运行速率缓慢时,它便将数据从RAM移动到称为“分页文件”的空间中。将数据移入分页文件可释放RAM,以便完成工作。一般而言,计算机的RAM容量越大,程序运行得越快。若计算机的速率由于RAM可用空间匮乏而减缓,则可尝试通过增加虚拟内存来进行补偿。但是,计算机从RAM读取数据的速率要比从硬盘读取数据的速率快,因而扩增RAM容量(可加内存条)是最佳选择。
虚拟内存是Windows作为内存使用的一部分硬盘空间,在硬盘上其实就是一个硕大无比的文件,文件名是PageFile.sys,通常状态下是看不到的。必须关闭资源管理器对系统文件的保护功能才能看到这个文件。虚拟内存有时候也被称为是“页面文件”就是从这个文件的文件名中来的。
进程隔离是为保护操作系统中进程互不干扰而设计的一组不同硬件和软件的技术。这个技术是为了避免进程A写入进程B的情况发生。进程的隔离实现使用了虚拟内存。进程A的虚拟地址和进程B的虚拟地址不同,这样就防止进程A将数据信息写入进程B。
进程隔离的安全性通过禁止进程间内存的访问可以方便实现。相比之下,一些不安全的操作系统(例如DOS)能够允许任何进程对其他进程的内存进行写操作。
进程调度(Process Scheduling)
在多进程的环境里,虽然从概念上看,有多个进程在同时执行,但在单个CPU下,在任何时刻只能有一个进程处于执行状态,而其他进程则处于非执行状态。那么问题来了,我们是如何确定在任意时刻到底由哪个进程执行,哪些不执行呢?这就涉及到进程管理的一个重要组成部分:进程调度。
进程调度是操作系统进程管理的一个重要组成部分,其任务是选择下一个要运行的进程。发生时钟中断是激发调度的其中一个可能。当调度结束,选择下一个要运行的进程之后,如果选择的进程不是现在正在运行的进程,CPU要进行上下文交换(Context Switching)。
我们必须解决如下问题:
- 使用某种调度算法安排多个进程顺序执行
- 对于目前正在休息的进程,需要让它在重新运行时,知道自己挂起之前的状态。于是作为进程本身,它是没有感觉自己被中断过的,即上下文交换
基本调度算法
一般的程序任务分为三种:CPU计算密集型、IO密集型与平衡(计算与IO各半)型,对于不同类型的程序,调度需要达到的目的也有所不同。对于IO密集型,响应时间最重要;对于CPU密集型,则周转时间最重要;而对于平衡型,进行某种响应和周转之间的平衡就显得比较重要。因此,进程调度的目标就是要达到极小化平均响应时间、极大化系统吞吐率、保持系统各个功能部件均处于繁忙状态和提供某种貌似公平的机制。
先来先服务(FCFS)算法
先来先服务(FCFS)算法是一种最常见的算法,它是人的本性中的一种公平观念。其优点就是简单且实现容易,缺点则是短的工作有可能变得很慢,因为其前面有很长的工作在执行,这样就会造成用户的交互式体验也比较差。例如排队办理业务时,你要办理的业务只需要几分钟就可以办好,但是你前面的一个人办理的事情很复杂需要1个小时,这时你需要在他后面等很久,于是你就想到:要是每个人轮流办理10分钟事务的话,那该多好!于是就出现了时间片轮转算法。
时间片轮转算法
时间片轮转是对FCFS算法的一种改进,其主要目的是改善短程序的响应时间,实现方式就是周期性地进行进程切换。时间片轮转的重点在于时间片的选择,需要考虑多方因素:如果运行的进程多时,时间片就需要短一些;进程数量少时,时间片就可以适当长一些。因此,时间片的选择是一个综合的考虑,权衡各方利益,进行适当折中。
但是,时间片轮转的系统响应时间也不一定总是比FCFS的响应时间短。时间片轮转是一种大锅饭的做法,但是现实生活中却是走的“一部分人先富,先富带动后富”的路线。例如,如果有30个任务,其中一个任务只需要1秒时间执行,而其他29个任务需要30秒钟执行,如果因为某种原因,这个只要1秒钟的任务排在另外29个任务的后面轮转,则它需要等待29秒钟才能执行(假定时间片为1秒)。于是,这个任务的响应时间和交互体验就变得非常差。因此,短任务优先算法被提出。
短任务优先算法
短任务优先算法的核心是所有的任务并不都一样,而是有优先级的区分。具体来说,就是短任务的优先级比长任务的高,而我们总是安排优先级高的任务先运行(这可能会导致饥饿)。
短任务优先算法又分为两种类型:一种是非抢占式,一种是抢占式。非抢占式当已经在CPU上运行的任务结束或阻塞时,从候选任务中选择执行时间最短的进程来执行。而抢占式则是每增加一个新的进程就需要对所有进程(包括正在CPU上运行的进程)进行检查,谁的时间短就运行谁。
由于短任务优先总是运行需要执行时间最短的程序,因此其系统平均响应时间在以上几种算法中是最优的,这也是短任务优先算法的优点。但短任务优先算法也有缺点:一是可能造成长任务永远无法得到CPU时间从而导致“饥饿”。二是如何知道每个进程还需要运转多久?于是为了解决第一个缺点,优先级调度算法被提出。而第二个缺点则可以采取一些启发式的方法来进行估算,目前很多的人工智能算法都可以做这个事。
优先级调度算法
优先级调度算法给每个进程赋予一个优先级,每次需要进程切换时,找一个优先级最高的进程进行调度。这样如果赋予长进程一个高优先级,则该进程就不会再“饥饿”。事实上,短任务优先算法本身就是一种优先级调度,只不过它给予短进程更高的优先级而已。
该算法的优点在于可以赋予重要的进程以高优先级以确保重要任务能够得到CPU时间,其缺点则有二:一是低优先级的进程可能会“饥饿”,二是响应时间无法保证。第一个缺点可以通过动态地调节任务的优先级解决,例如一个进程如果等待时间过长,其优先级将持续提升,超越其他进程的优先级,从而得到CPU时间。第二个缺点可以通过将一个进程优先级设置为最高来解决,但即使将优先级设置为最高,但如果每个人都将自己的进程优先级设置为最高,其响应时间还是无法保证。
混合调度算法
之前的算法都存在一定缺点,那么可否有一个算法混合他们的优点,摒弃它们的缺点,这就是所谓的混合调度算法。混合调度算法将所有进程分为不同的大类,每个大类为一个优先级。如果两个进程处于不同的大类,则处于高优先级大类的进程优先执行;如果处于同一个大类,采用时间片轮转算法来执行。
进程状态保存
当发生上下文切换时,进程和CPU互动的资料(存储在CPU的寄存器中)需要保存。因为这些资料很快就会被另一个进程覆盖(通过和CPU的互动)。这些资料一般以名为进程控制块(Process Control Block,PCB)的数据结构储存起来。一般来说,这些资料应该包含:寄存器、程序计数器、状态字、栈指针、优先级、进程ID、创建时间、所耗CPU时间、当前持有的各种句柄等等。
进程自己的资料是不需要保存的,因为它储存在内存中一块固定的空间,而且进程隔离已经保证了这些资料是安全的。当进程恢复运行时,它只需要再次回到属于它的空间就可以找到上次所有的资料。
上下文切换(Context Switching)
简单的说,上下文切换就是当进程调度导致要切换进程时,当前运行的进程将资料储存进进程控制块,然后通过调度算法选择下一个进程,被选中的(当前在睡眠)的进程从进程控制块中获得自己之前工作的信息,然后重新开始工作的过程。
在三种情况下可能会发生上下文切换:中断处理,多任务处理,用户态切换。在中断处理中,其他程序的行为打断了当前正在运行的程序。当CPU接收到中断请求时,会在正在运行的程序和发起中断请求的程序之间进行一次上下文切换。在多任务处理中,CPU会在不同程序之间来回切换,每个程序都有相应的处理时间片,CPU在两个时间片的间隔中进行上下文切换。用户态切换则是用户自己的行为,例如从游戏中切出来,看一会网站的行为。
上下文切换通常是计算密集型的。也就是说,它需要相当可观的处理器时间,会对性能造成负面影响。相似的,线程也存在上下文切换。
进程间通信(Inter-Process Communication)
进程间通信(IPC,Inter-Process Communication),指至少两个进程或线程间传送数据或信号的一些技术或方法。每个进程都有自己的一部分独立的系统资源,彼此是隔离的。为了能使不同的进程互相访问资源并进行协调工作,才有了进程间通信。举一个典型的例子,使用进程间通信的两个应用可以被分类为客户端和服务器,客户端进程请求数据,服务端回复客户端的数据请求。有一些应用本身既是服务器又是客户端,这在分布式计算中,时常可以见到。这些进程可以运行在同一计算机上或网络连接的不同计算机上。
管道(Pipeline)
在类Unix操作系统(以及一些扩展例如Windows)中,管道(Pipeline)是原始的软件管道:即是一个由标准输入输出链接起来的进程集合,所以每一个进程的输出(stdout)被直接作为下一个进程的输入(stdin)。管道所占的空间既可以是内存也可以是磁盘。
要创建一个管道,一个进程只需要调用管道创建的系统调用(系统API)即可,该系统调用所做的事情就是在某种存储介质上划出一片空间,赋给其中一个进程写的权利,另一个进程读的权利。
C#中类似的通信机制可以参考此文:http://www.cnblogs.com/yukaizhao/archive/2011/08/04/system-io-pipes.html
套接字(Socket)
套接字(Socket)的功能非常强大,可以支持不同层面、不同应用、跨网络的通信。使用套接字进行通信需要双方均创建一个套接字,其中一方作为服务器方,另外一方作为客户方。服务器方必须首先创建一个服务区套接字,然后在该套接字上进行监听,等待远方的连接请求。客户方也要创建一个套接字,然后向服务器方发送连接请求。服务器套接字在受到连接请求之后,将在服务器方机器上新建一个客户套接字,与远方的客户方套接字形成点到点的通信通道。之后,客户方和服务器方便可以直接通过类似于send和recv的命令在这个创建的套接字管道上进行交流了。
信号与信号量
信号类似于我们生活中的电报,如果你想给某人发一封电报,就拟好电文,然后将电文和收报人的信息都交给电报公司。电报公司则将电报发送到收报人所在地的邮局,并通知收报人来取电报。其中,发报文时无需收报人事先知道,也无需进行任何协调。如果对方选择不对信号做出响应,则将被OS终止运行。
在计算机中,信号就是一个内核对象或者是一个内核数据结构。发送方将该数据结构的内容填好,并指明该信号的目标进程后,发出特定的软件中断(这就是一个发电报的操作)。OS接收到特定的中断请求后,知道是有进程要发送信号,于是到特定的内核数据结构里查找信号接收方,并进行通知。接到通知的进程则对信号进行相应处理。
信号量来源于铁路的运行:在一条单轨铁路上,任何时候只允许有一列火车行驶在该铁路上,而管理这条铁路的系统就是信号量。任何一列火车必须等到表明该铁路可以行驶的信号后才能进入轨道。当列车进入后,需要将信号改为禁止状态进入来防止别的列车同时进入。而当列车驶出单轨后,则需要将信号变回允许进入状态,这很像以前的旗语。当然,通过联想到我们实际开发中经常用的锁,这就更容易理解了。
在计算机中,信号量实际上就是一个简单整数。一个进程在信号变为0或1的情况下推进,并将信号变为1或0来防止别的进程同时推进。当该进程完成任务后,则将信号再改为0或1,从而允许其他进程执行。从而我们也可以看出,信号量已经不只是一种通信机制,更是一种同步机制。
在系统中,给予每一个进程一个信号量,代表每个进程目前的状态,未得到控制权的进程会在特定地方被强迫停下来,等待可以继续进行的信号到来。如果信号量是一个任意的整数,通常被称为计数信号量(Counting semaphore),或一般信号量(general semaphore);如果信号量只有二进制的0或1,称为二进制信号量(binary semaphore)。在linux系中,二进制信号量(binary semaphore)又称Mutex。
计数信号量具备两种操作动作,之前称为V(又称signal())与P(wait())。V操作会增加信号量S的数值,P操作会减少它。
运作方式:
- 初始化,给它一个非负数的整数值。
- 运行 P(wait()),信号量S的值将被减少。企图进入临界区块的进程,需要先运行 P(wait())。当信号量S减为负值时,进程会被挡住,不能继续;当信号量S不为负值时,进程可以获准进入临界区块。(信号量通过这个机制,控制进入临界区块的进程的数量)
- 运行 V(又称signal()),信号量S的值会被增加。结束离开临界区块的进程,将会运行 V(又称signal())。当信号量S不为负值时,先前被挡住的其他进程,将可获准进入临界区块。
C#对信号量的实现是Mutex和Semaphore。
共享内存
两个进程共同拥有同一片内存。对于这片内存中的任何内容,二者均可以访问。要使用共享内存进行通信,进程A首先需要创建一片内存空间作为通信用,而其他进程B则将这片内存映射到自己的(虚拟)地址空间。这样,进程A读写自己地址空间中对应共享内存的区域时,就是在和进程B进行通信。
消息队列
消息队列是一列具有头和尾的消息排列,新来的消息放在队列尾部,而读取消息则从队列头部开始。
这样看来,它和管道十分类似,一头读,一头写?的确,看起来很像管道,但又不是管道:
(1)消息队列无固定的读写进程,任何进程都可以读写;而管道需要指定谁读和谁写;
(2)消息队列可以同时支持多个进程,多个进程可以读写消息队列;即所谓的多对多,而管道是点对点;
(3)消息队列只在内存中实现,而管道还可以在磁盘上实现;
线程
线程是CPU的一个虚拟。
微软决定在一个进程中运行应用程序的每个实例。进程是应用程序要使用的资源的一个集合。当程序在进程中开始运行时,它就如同被关进了一个密闭的空间,里面有所有它需要的东西。不同的密闭空间不会发生关系,任何一个进程死掉不会导致整个系统崩溃。进程有自己的虚拟地址空间,确保这个进程使用的代码不会被其他进程访问。并且当进程失去响应时,系统仍然可以工作,还可以用其他进程杀死失去响应的进程。
听起来似乎没问题?但虽然应用程序与操作系统之间已经透过进程来达到隔离和保护的效果,可是它们仍然有共享的资源:CPU。如果机器只有一个CPU,那么当某个应用程序进入无穷循环,那个唯一的CPU就会忙着跑无穷循环而无暇照顾其他应用程序,形同锁住。于是,用户会发现每个应用程序都无法响应了,无论鼠标点在哪里都不起作用。为了解决这个CPU无法分身的问题,线程(thread)便应运而生。
Windows通过线程来虚拟化CPU。线程使得每个进程(至少拥有一个线程)拥有CPU的一个“分身”(称为逻辑CPU,真正的CPU称为物理CPU)。当一个线程进入无限循环时,其他线程仍然可以继续运行。
线程的好处如下:
1. 在很多现代的大型程序(例如Word)中,一个程序同时在做很多事情。当我们使用Microsoft Word时,实际上是打开了多个线程。这些线程一个负责显示,一个负责接收输入,一个定时进行存盘。这些线程一起运转,让我们感觉到输入和显示同时发生,而不用键入一些字符等待一会儿才显示到屏幕上。在不经意间,Word还能定期自动保存。这需要多个线程互相同步或互斥的并行完成工作,而将这些工作分解到不同的线程中去无疑简化了编程模型。
2. 因为线程相比进程来说更加轻量,所以线程的创建和销毁的代价更小。
3. 线程提高了性能,虽然线程宏观上是并行的,但微观上却是串行(通过时间片)。从CPU角度线程无法提升性能,但如果某些线程涉及到等待资源(比如IO,等待输入)时,多线程允许进程中的其它线程继续执行而不是整个进程被阻塞,因此提高了CPU的利用率,从这个角度会提升性能(和多道程序设计相同)。
4. 在多CPU或多核的情况下,使用线程不仅仅在宏观上并行,在微观上也是并行的。
在线程模式下,一个进程至少有一个线程,也可以有多个线程。线程和进程既有相似又有不同之处。
相似:
- 线程可以看成是轻量级的进程。所以使用了进程之后,再引入线程并不能提高性能。
- 不同的线程和不同的进程都可以通信。
- 线程和进程都有优先级,都需要系统进行调度。
- 线程和进程都有状态。
不同之处:
- 线程的任务是虚拟化CPU,令CPU不再是所有进程共享的一个互斥元件,进程的任务则是提高CPU的使用效率。
- 线程作为调度和分派的基本单位,而进程作为资源拥有的基本单位。同一进程中,线程的切换不会引起进程的切换,但从一个进程中的线程切换到另外一个进程中的线程时,将会引起进程切换。
- 一个进程可以包括多个线程,而且至少要有一个前台线程。
- 进程之间通过虚拟内存隔离,但一个进程中的线程共享所有进程的资源。
同进程一样,线程也需要调度,通信和同步。
参考资料
http://www.cnblogs.com/edisonchou/p/5022508.html
https://zh.wikipedia.org/wiki/%E8%A1%8C%E7%A8%8B%E9%96%93%E9%80%9A%E8%A8%8A
http://www.cnblogs.com/edisonchou/p/5037403.html
http://www.cnblogs.com/CareySon/archive/2012/05/04/ProcessAndThread.html
百度百科