欢迎光临散文网 会员登陆 & 注册

不会吧!不会吧!还有人搞不懂进程和线程!

2022-02-08 19:44 作者:补给站Linux内核  | 我要投稿

进程

我们都知道计算机的核心是CPU,它承担了所有的计算任务,而操作系统是计算机的管理者,它负责任务的调度,资源的分配和管理,

统领整个计算机硬件;应用程序是具有某种功能的程序,程序是运行于操作系统之上的。

进程是一个具有一定独立功能的程序在一个数据集上的一次动态执行的过程,是操作系统进行资源分配和调度的一个独立单位,是应用

程序运行的载体。进程是一种抽象的概念,从来没有统一的标准定义。进程一般由程序,数据集合和进程控制块三部分组成。程序用于描述

进程要完成的功能,是控制进程执行的指令集;数据集合是程序在执行时所需要的数据和工作区;程序控制块包含进程的描述信息和控制信息

是进程存在的唯一标志

进程具有的特征:

  1. 动态性:进程是程序的一次执行过程,是临时的,有生命期的,是动态产生,动态消亡的;

  2. 并发性:任何进程都可以同其他进行一起并发执行;

  3. 独立性:进程是系统进行资源分配和调度的一个独立单位;

  4. 结构性:进程由程序,数据和进程控制块三部分组成

避免在一台机器上同时运行同一应用程序的多个实例

有些应用程序需要这种功能。实际上,通常来说在同一台机器上同时运行一个应用程序的多个实例并没有意义。

直到现在,为了在Windows下满足上述约束,开发者最常用的方法仍然是使用有名互斥体(named mutex)技术。然而采用这种技术来满足上述约束存在以下缺点:

  • 该技术具有使互斥体的名字被其他应用程序所使用的较小的、潜在的风险。在这种情况下该技术将不再有效并且会造成很难检测到的bug。

  • 该技术不能解决我们仅允许一个应用程序产生N个实例这种一般的问题。

幸而在System.Diagnostics.Process类中拥有GetCurrentProcess()(返回当前进程)和GetPro- cesses()(返回机器上所有的进程)这样的静态方法。在下面的程序中我们为上述问题找到了一个优雅且简单的解决方案。


通过方法参数指定了远程机器的名字后,GetProcesses()方法也可以返回远程机器上所有的进程。

终止当前进程

可以调用System.Environment类中的静态方法Exit(int exitCode)或FailFast(stringmessage)终止当前进程。Exit()方法是最好的选择,它将彻底终止进程并向操作系统返回指定的退出代码值。之所以称为彻底终止是因为当前对象的所有清理工作以及finally块的执行都将由不同的线程完成。当然,终止进程将花费一定的时间。

顾名思义,FailFast()方法可以迅速终止进程。Exit()方法所做的预防措施将被它忽略。只有一个包含了指定信息的严重错误会被操作系统记录到日志中。你可能想要在探查问题的时候使用该方法,因为可以将该程序的彻底终止视为数据恶化的起因。

线程

  在早期的操作系统中并没有线程的概念,进程是拥有资源和独立运行的最小单位,也是程序执行的最小单位。任务调度采用的是时间片

轮转的抢占式调度方式,而进程是任务调度的最小单位,每个进程有各自独立的一块内存,使得各个进程之间内存地址相互隔离。

  后来,随着计算机的发展,对CPU的要求越来越高,进程之间的切换开销较大,已经无法满足越来越复杂的程序的要求了。于是就发明

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

多个线程,各个线程之间共享程序的内存空间(也就是所在进程的内存空间)。一个标准的线程由线程ID,当前指令指针PC,寄存器和堆栈组

成。而进程由内存空间(代码,数据,进程空间,打开的文件)和一个或多个线程组成。

受托管的线程与 Windows线程

必须要了解,执行.NET应用的线程实际上仍然是Windows线程。但是,当某个线程被CLR所知时,我们将它称为受托管的线程。具体来说,由受托管的代码创建出来的线程就是受托管的线程。如果一个线程由非托管的代码所创建,那么它就是非托管的线程。不过,一旦该线程执行了受托管的代码它就变成了受托管的线程。

一个受托管的线程和非托管的线程的区别在于,CLR将创建一个System.Threading.Thread类的实例来代表并操作前者。在内部实现中,CLR将一个包含了所有受托管线程的列表保存在一个叫做ThreadStore地方。

CLR确保每一个受托管的线程在任意时刻都在一个AppDomain中执行,但是这并不代表一个线程将永远处在一个AppDomain中,它可以随着时间的推移转到其他的AppDomain中。关于AppDomain的概念参见4.1。

从安全的角度来看,一个受托管的线程的主用户与底层的非托管线程中的Windows主用户是无关的。

抢占式多任务处理

我们可以问自己下面这个问题: 我的计算机只有一个处理器,然而在任务管理器中我们却可以看到数以百计的线程正同时运行在机器上!这怎么可能呢?

多亏了抢占式多任务处理,通过它对线程的调度,使得上述问题成为可能。调度器作为Windows内核的一部分,将时间切片,分成一段段的时间片。这些时间间隔以毫秒为精度且长度并不固定。针对每个处理器,每个时间片仅服务于单独一个线程。线程的迅速执行给我们造成了它们在同时运行的假象。我们在两个时间片的间隔中进行上下文切换。该方法的优点在于,那些正在等待某些Windows资源的线程将不会浪费时间片,直到资源有效为止。

之所以用抢占式这个形容词来修饰这种多任务管理方式,是因为在此种方式下线程将被系统强制性中断。那些对此比较好奇的人应该了解到,在上下文切换的过程中,操作系统会在下一个线程将要执行的代码中插入一条跳转到下一个上下文切换的指令。该指令是一个软中断,如果线程在遇到这条指令前就终止了(例如,它正在等待某个资源),那么该指定将被删除而上下文切换也将提前发生。

抢占式多任务处理的主要缺点在于,必须使用一种同步机制来保护资源以避免它们被无序访问。除此之外,还有另一种多任务管理模型,被称为协调式多任务管理,其中线程间的切换将由线程自己负责完成。该模型普遍认为太过危险,原因在于线程间的切换不发生的风险太大。如我们在4.2.8节中所解释的那样,该机制会在内部使用以提升某些服务器的性能,例如SQL Server2005。但Windows操作系统仅仅实现了抢占式多任务处理。

进程与线程的优先级

某些任务拥有比其他任务更高的优先级,它们需要操作系统为它们申请更多的处理时间。例如,某些由主处理器负责的外围驱动器必须不能被中断。另一类高优先级的任务就是图形用户界面。事实上,用户不喜欢等待用户界面被重绘。

那些从Win32世界来的用户知道在CLR的底层,也就是Windows操作系统中,可以为每个线程赋予一个0~31的优先级。但你无法在.NET的世界中也使用这些数值,因为:

  • 它们无法描述自身的含义。

  • 随着时间的流逝这些值是非常容易变化的。

1. 进程的优先级

可以使用Process类中的类型为ProcessPriorityClass的PriorityClass{get;set;}属性为进程赋予一个优先级。System.Diagnostics.ProcessPriorityClass枚举包含以下值:



如果某个进程中属于Process类的PriorityBoostEnabled属性的值为true(默认值为true),那么当该进程占据前台窗口的时候,它的优先级将增加一个单位。只有当Process类的实例引用的是本机进程时,才能够访问该属性。

可以通过以下操作利用任务管理器来改变一个进程的优先级:在所选的进程上点击右键>设置优先级>从提供的6个值(和上图所述一致)中做出选择。

Windows操作系统有一个优先级为0的空闲进程。该进程不能被其他任何进程使用。根据定义,进程的活跃度用时间的百分比表示为:100%减去在空闲进程中所耗费时间的比率。

2. 线程的优先级

每个线程可以结合它所属进程的优先级,并使用System.Threading.Thread类中类型为ThreadPriority的Priority{get;set;}属性定义各自的优先级。System.Threading.Thread- Priority包含以下枚举值:



在大多数应用程序中,不需要修改进程和线程的优先级,它们的默认值为Normal。

进程与线程的区别

1. 线程是程序执行的最小单位,而进程是操作系统分配资源的最小单位;

2. 一个进程由一个或多个线程组成,线程是一个进程中代码的不同执行路线

3. 进程之间相互独立,但同一进程下的各个线程之间共享程序的内存空间(包括代码段,数据集,堆等)及一些进程级的资源(如打开文件和信

号等),某进程内的线程在其他进程不可见;

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

一个程序至少一个进程, 一个进程至少一个线程

进程有自己的独立地址空间, 每启动一个进程, 系统就会为它分配地址空间, 建立数据表来维护代码段、堆栈段和数据段, 这种操作非常昂贵。

而线程是共享进程中的数据的, 使用相同的地址空间, 因此CPU切换一个线程的花费远比进程要小很多, 同时创建一个线程的开销啊也比进程要小很多。

线程之间的通信更方便, 同一进程下的线程共享全局变量、静态变量等数据, 而进程之间的通信需要以通信的方式进行。

如何处理好同步与互斥是编写多线程程序的难点。

多进程程序更健壮, 进程有独立的地址空间, 一个进程崩溃后, 在保护模式下不会对其它进程产生影响,

而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量, 但线程之间没有独立的空间, 所以可能一个线程出现问题, 进而导致整个程序出现问题

线程和进程关系示意图



总之,线程和进程都是一种抽象的概念,线程是一种比进程还小的抽象,线程和进程都可用于实现并发。

在早期的操作系统中并没有线程的概念,进程是能拥有资源和独立运行的最小单位,也是程序执行的最小单位,它相当于

一个进程里只有一个线程,进程本身就是线程。所以线程有时被称为轻量级进程

  后来,随着计算机的发展,对多个任务之间上下文切换的效率要求越来越高,就抽象出一个更小的概念-线程,一般一个进程会有多个

(也可以是一个)线程。


任务调度

  大部分操作系统的任务调度是采用时间片轮转的抢占式调度方式,也就是说一个任务执行一小段时间后强制暂停去执行下一个任务,每个

任务轮流执行。任务执行的一小段时间叫做时间片,任务正在执行时的状态叫运行状态,任务执行一段时间后强制暂停去执行下一个任务,被

暂停的任务就处于就绪状态,等待下一个属于它的时间片的到来。这样每个任务都能得到执行,由于CPU的执行效率非常高,时间片

非常短,在各个任务之间快速地切换,给人的感觉就是多个任务在“同时进行”,这也就是我们所说的并发

为何不使用多进程而是使用多线程?

    线程廉价,线程启动比较快,退出比较快,对系统资源的冲击也比较小。而且线程彼此分享了大部分核心对象(File Handle)的拥有权

    如果使用多重进程,但是不可预期,且测试困难


不会吧!不会吧!还有人搞不懂进程和线程!的评论 (共 条)

分享到微博请遵守国家法律