一文讀懂什么是進程巍举、線程、協(xié)程

姓名:張志文 學號:19021210649
【嵌牛導讀】
計算機中的進程凝垛、線程懊悯、協(xié)程有著許多的聯(lián)系和區(qū)別
【嵌牛鼻子】
進程;線程梦皮;協(xié)程
【嵌牛提問】
什么是進程炭分、線程、協(xié)程
【嵌牛正文】

進程

我們都知道計算機的核心是CPU剑肯,它承擔了所有的計算任務捧毛;而操作系統(tǒng)是計算機的管理者,它負責任務的調度退子、資源的分配和管理岖妄,統(tǒng)領整個計算機硬件;應用程序則是具有某種功能的程序寂祥,程序是運行于操作系統(tǒng)之上的荐虐。

進程是一個具有一定獨立功能的程序在一個數(shù)據(jù)集上的一次動態(tài)執(zhí)行的過程,是操作系統(tǒng)進行資源分配和調度的一個獨立單位丸凭,是應用程序運行的載體福扬。進程是一種抽象的概念,從來沒有統(tǒng)一的標準定義惜犀。

進程一般由程序铛碑、數(shù)據(jù)集合和進程控制塊三部分組成。

  • 程序用于描述進程要完成的功能虽界,是控制進程執(zhí)行的指令集汽烦;
  • 數(shù)據(jù)集合是程序在執(zhí)行時所需要的數(shù)據(jù)和工作區(qū);
  • 程序控制塊(Program Control Block莉御,簡稱PCB)撇吞,包含進程的描述信息和控制信息俗冻,是進程存在的唯一標志。

進程具有的特征:

  • 動態(tài)性:進程是程序的一次執(zhí)行過程牍颈,是臨時的迄薄,有生命期的,是動態(tài)產生煮岁,動態(tài)消亡的讥蔽;
  • 并發(fā)性:任何進程都可以同其他進程一起并發(fā)執(zhí)行;
  • 獨立性:進程是系統(tǒng)進行資源分配和調度的一個獨立單位画机;
  • 結構性:進程由程序冶伞、數(shù)據(jù)和進程控制塊三部分組成。

線程

在早期的操作系統(tǒng)中并沒有線程的概念色罚,進程是能擁有資源和獨立運行的最小單位碰缔,也是程序執(zhí)行的最小單位账劲。任務調度采用的是時間片輪轉的搶占式調度方式戳护,而進程是任務調度的最小單位,每個進程有各自獨立的一塊內存瀑焦,使得各個進程之間內存地址相互隔離腌且。

后來,隨著計算機的發(fā)展榛瓮,對CPU的要求越來越高铺董,進程之間的切換開銷較大,已經無法滿足越來越復雜的程序的要求了禀晓。于是就發(fā)明了線程精续。

線程是程序執(zhí)行中一個單一的順序控制流程,是程序執(zhí)行流的最小單元粹懒,是處理器調度和分派的基本單位重付。一個進程可以有一個或多個線程,各個線程之間共享程序的內存空間(也就是所在進程的內存空間)凫乖。一個標準的線程由線程ID确垫、當前指令指針(PC)、寄存器和堆棧組成帽芽。而進程由內存空間(代碼删掀、數(shù)據(jù)、進程空間导街、打開的文件)和一個或多個線程組成披泪。
(讀到這里可能有的讀者迷糊,感覺這和Java的內存空間模型不太一樣搬瑰,但如果你深入的讀過深入理解Java虛擬機這本書的話你就會恍然大悟)

![<meta charset="utf-8">

進程

我們都知道計算機的核心是CPU款票,它承擔了所有的計算任務计福;而操作系統(tǒng)是計算機的管理者,它負責任務的調度徽职、資源的分配和管理象颖,統(tǒng)領整個計算機硬件;應用程序則是具有某種功能的程序姆钉,程序是運行于操作系統(tǒng)之上的说订。

進程是一個具有一定獨立功能的程序在一個數(shù)據(jù)集上的一次動態(tài)執(zhí)行的過程,是操作系統(tǒng)進行資源分配和調度的一個獨立單位潮瓶,是應用程序運行的載體陶冷。進程是一種抽象的概念,從來沒有統(tǒng)一的標準定義毯辅。

進程一般由程序埂伦、數(shù)據(jù)集合和進程控制塊三部分組成。

  • 程序用于描述進程要完成的功能思恐,是控制進程執(zhí)行的指令集沾谜;
  • 數(shù)據(jù)集合是程序在執(zhí)行時所需要的數(shù)據(jù)和工作區(qū);
  • 程序控制塊(Program Control Block胀莹,簡稱PCB)基跑,包含進程的描述信息和控制信息,是進程存在的唯一標志描焰。

進程具有的特征:

  • 動態(tài)性:進程是程序的一次執(zhí)行過程媳否,是臨時的,有生命期的荆秦,是動態(tài)產生篱竭,動態(tài)消亡的;
  • 并發(fā)性:任何進程都可以同其他進程一起并發(fā)執(zhí)行步绸;
  • 獨立性:進程是系統(tǒng)進行資源分配和調度的一個獨立單位掺逼;
  • 結構性:進程由程序、數(shù)據(jù)和進程控制塊三部分組成靡努。

線程

在早期的操作系統(tǒng)中并沒有線程的概念坪圾,進程是能擁有資源和獨立運行的最小單位,也是程序執(zhí)行的最小單位惑朦。任務調度采用的是時間片輪轉的搶占式調度方式兽泄,而進程是任務調度的最小單位,每個進程有各自獨立的一塊內存漾月,使得各個進程之間內存地址相互隔離病梢。

后來,隨著計算機的發(fā)展,對CPU的要求越來越高蜓陌,進程之間的切換開銷較大觅彰,已經無法滿足越來越復雜的程序的要求了。于是就發(fā)明了線程钮热。

線程是程序執(zhí)行中一個單一的順序控制流程填抬,是程序執(zhí)行流的最小單元,是處理器調度和分派的基本單位隧期。一個進程可以有一個或多個線程飒责,各個線程之間共享程序的內存空間(也就是所在進程的內存空間)。一個標準的線程由線程ID仆潮、當前指令指針(PC)宏蛉、寄存器和堆棧組成。而進程由內存空間(代碼性置、數(shù)據(jù)拾并、進程空間、打開的文件)和一個或多個線程組成鹏浅。
(讀到這里可能有的讀者迷糊嗅义,感覺這和Java的內存空間模型不太一樣,但如果你深入的讀過深入理解Java虛擬機這本書的話你就會恍然大悟)

image

如上圖篡石,在任務管理器的進程一欄里芥喇,有道詞典和有道云筆記就是進程,而在進程下又有著多個執(zhí)行不同任務的線程凰萨。

任務調度

線程是什么?要理解這個概念械馆,需要先了解一下操作系統(tǒng)的一些相關概念胖眷。大部分操作系統(tǒng)(如Windows、Linux)的任務調度是采用時間片輪轉的搶占式調度方式霹崎。

在一個進程中珊搀,當一個線程任務執(zhí)行幾毫秒后,會由操作系統(tǒng)的內核(負責管理各個任務)進行調度尾菇,通過硬件的計數(shù)器中斷處理器境析,讓該線程強制暫停并將該線程的寄存器放入內存中,通過查看線程列表決定接下來執(zhí)行哪一個線程派诬,并從內存中恢復該線程的寄存器劳淆,最后恢復該線程的執(zhí)行,從而去執(zhí)行下一個任務默赂。
上述過程中沛鸵,任務執(zhí)行的那一小段時間叫做時間片,任務正在執(zhí)行時的狀態(tài)叫運行狀態(tài),被暫停的線程任務狀態(tài)叫做就緒狀態(tài)曲掰,意為等待下一個屬于它的時間片的到來疾捍。

這種方式保證了每個線程輪流執(zhí)行,由于CPU的執(zhí)行效率非常高栏妖,時間片非常短乱豆,在各個任務之間快速地切換,給人的感覺就是多個任務在“同時進行”吊趾,這也就是我們所說的并發(fā)(別覺得并發(fā)有多高深咙鞍,它的實現(xiàn)很復雜,但它的概念很簡單趾徽,就是一句話:多個任務同時執(zhí)行)续滋。多任務運行過程的示意圖如下:

image

進程與線程的區(qū)別

前面講了進程與線程,但可能你還覺得迷糊孵奶,感覺他們很類似疲酌。的確,進程與線程有著千絲萬縷的關系了袁,下面就讓我們一起來理一理:

  1. 線程是程序執(zhí)行的最小單位朗恳,而進程是操作系統(tǒng)分配資源的最小單位;
  2. 一個進程由一個或多個線程組成载绿,線程是一個進程中代碼的不同執(zhí)行路線粥诫;
  3. 進程之間相互獨立,但同一進程下的各個線程之間共享程序的內存空間(包括代碼段崭庸、數(shù)據(jù)集怀浆、堆等)及一些進程級的資源(如打開文件和信號),某進程內的線程在其它進程不可見怕享;
  4. 調度和切換:線程上下文切換比進程上下文切換要快得多执赡。

線程與進程關系的示意圖:

image
image

總之,線程和進程都是一種抽象的概念函筋,線程是一種比進程更小的抽象沙合,線程和進程都可用于實現(xiàn)并發(fā)。
在早期的操作系統(tǒng)中并沒有線程的概念跌帐,進程是能擁有資源和獨立運行的最小單位首懈,也是程序執(zhí)行的最小單位。它相當于一個進程里只有一個線程谨敛,進程本身就是線程究履。所以線程有時被稱為輕量級進程(Lightweight Process,LWP)佣盒。

image

后來挎袜,隨著計算機的發(fā)展,對多個任務之間上下文切換的效率要求越來越高,就抽象出一個更小的概念——線程盯仪,一般一個進程會有多個(也可是一個)線程紊搪。

image

多線程與多核

上面提到的時間片輪轉的調度方式說一個任務執(zhí)行一小段時間后強制暫停去執(zhí)行下一個任務,每個任務輪流執(zhí)行全景。很多操作系統(tǒng)的書都說“同一時間點只有一個任務在執(zhí)行”耀石。那有人可能就要問雙核處理器呢?難道兩個核不是同時運行嗎爸黄?

其實“同一時間點只有一個任務在執(zhí)行”這句話是不準確的滞伟,至少它是不全面的。那多核處理器的情況下炕贵,線程是怎樣執(zhí)行呢梆奈?這就需要了解內核線程。

多核(心)處理器是指在一個處理器上集成多個運算核心從而提高計算能力称开,也就是有多個真正并行計算的處理核心亩钟,每一個處理核心對應一個內核線程。
內核線程(Kernel Thread鳖轰,KLT)就是直接由操作系統(tǒng)內核支持的線程清酥,這種線程由內核來完成線程切換,內核通過操作調度器對線程進行調度蕴侣,并負責將線程的任務映射到各個處理器上焰轻。一般一個處理核心對應一個內核線程,比如單核處理器對應一個內核線程昆雀,雙核處理器對應兩個內核線程辱志,四核處理器對應四個內核線程。

現(xiàn)在的電腦一般是雙核四線程忆肾、四核八線程荸频,是采用超線程技術將一個物理處理核心模擬成兩個邏輯處理核心,對應兩個內核線程客冈,所以在操作系統(tǒng)中看到的CPU數(shù)量是實際物理CPU數(shù)量的兩倍,如你的電腦是雙核四線程稳强,打開“任務管理器\性能”可以看到4個CPU的監(jiān)視器场仲,四核八線程可以看到8個CPU的監(jiān)視器。

image

超線程技術就是利用特殊的硬件指令退疫,把一個物理芯片模擬成兩個邏輯處理核心渠缕,讓單個處理器都能使用線程級并行計算,進而兼容多線程操作系統(tǒng)和軟件褒繁,減少了CPU的閑置時間亦鳞,提高的CPU的運行效率。這種超線程技術(如雙核四線程)由處理器硬件的決定,同時也需要操作系統(tǒng)的支持才能在計算機中表現(xiàn)出來燕差。

程序一般不會直接去使用內核線程遭笋,而是去使用內核線程的一種高級接口——輕量級進程(Lightweight Process,LWP)徒探,輕量級進程就是我們通常意義上所講的線程瓦呼,也被叫做用戶線程。由于每個輕量級進程都由一個內核線程支持测暗,因此只有先支持內核線程央串,才能有輕量級進程。用戶線程與內核線程的對應關系有三種模型:一對一模型碗啄、多對一模型质和、多對多模型,在這以4個內核線程稚字、3個用戶線程為例對三種模型進行說明饲宿。

一對一模型

對于一對一模型來說,一個用戶線程就唯一地對應一個內核線程(反過來不一定成立尉共,一個內核線程不一定有對應的用戶線程)褒傅。這樣,如果CPU沒有采用超線程技術(如四核四線程的計算機)袄友,一個用戶線程就唯一地映射到一個物理CPU的內核線程殿托,線程之間的并發(fā)是真正的并發(fā)。一對一模型使用戶線程具有與內核線程一樣的優(yōu)點剧蚣,一個線程因某種原因阻塞時其他線程的執(zhí)行不受影響支竹;此處,一對一模型也可以讓多線程程序在多處理器的系統(tǒng)上有更好的表現(xiàn)鸠按。

但一對一模型也有兩個缺點:

  1. 許多操作系統(tǒng)限制了內核線程的數(shù)量礼搁,因此一對一模型會使用戶線程的數(shù)量受到限制;
  2. 許多操作系統(tǒng)內核線程調度時目尖,上下文切換的開銷較大馒吴,導致用戶線程的執(zhí)行效率下降。
image

多對一模型

多對一模型將多個用戶線程映射到一個內核線程上瑟曲,線程之間的切換由用戶態(tài)的代碼來進行饮戳,系統(tǒng)內核感受不到線程的實現(xiàn)方式。用戶線程的建立洞拨、同步扯罐、銷毀等都在用戶態(tài)中完成,不需要內核的介入烦衣。因此相對一對一模型歹河,多對一模型的線程上下文切換速度要快許多掩浙;此外,多對一模型對用戶線程的數(shù)量幾乎無限制秸歧。

但多對一模型也有兩個缺點:

  1. 如果其中一個用戶線程阻塞厨姚,那么其它所有線程都將無法執(zhí)行,因為此時內核線程也隨之阻塞了寥茫;
  2. 在多處理器系統(tǒng)上遣蚀,處理器數(shù)量的增加對多對一模型的線程性能不會有明顯的增加,因為所有的用戶線程都映射到一個處理器上了纱耻。
image

多對多模型

多對多模型結合了一對一模型和多對一模型的優(yōu)點芭梯,將多個用戶線程映射到多個內核線程上。由線程庫負責在可用的可調度實體上調度用戶線程弄喘,這使得線程的上下文切換非尘链快,因為它避免了系統(tǒng)調用蘑志。但是增加了復雜性和優(yōu)先級倒置的可能性累奈,以及在用戶態(tài)調度程序和內核調度程序之間沒有廣泛(且高昂)協(xié)調的次優(yōu)調度。

多對多模型的優(yōu)點有:

  1. 一個用戶線程的阻塞不會導致所有線程的阻塞急但,因為此時還有別的內核線程被調度來執(zhí)行澎媒;
  2. 多對多模型對用戶線程的數(shù)量沒有限制;
  3. 在多處理器的操作系統(tǒng)中波桩,多對多模型的線程也能得到一定的性能提升戒努,但提升的幅度不如一對一模型的高囤萤。
image

在現(xiàn)在流行的操作系統(tǒng)中永乌,大都采用多對多的模型。

查看進程與線程

一個應用程序可能是多線程的驯镊,也可能是多進程的萤皂,如何查看呢撒穷?在Windows下我們只須打開任務管理器就能查看一個應用程序的進程和線程數(shù)。按“Ctrl+Alt+Del”或右鍵快捷工具欄打開任務管理器裆熙。

查看進程數(shù)和線程數(shù):

image

在“進程”選項卡下端礼,我們可以看到一個應用程序包含的線程數(shù)入录。如果一個應用程序有多個進程齐媒,我們能看到每一個進程,如在上圖中纷跛,Google的Chrome瀏覽器就有多個進程。同時邀杏,如果打開了一個應用程序的多個實例也會有多個進程贫奠,如上圖中我打開了兩個cmd窗口唬血,就有兩個cmd進程。如果看不到線程數(shù)這一列唤崭,可以再點擊“查看\選擇列”菜單拷恨,增加監(jiān)聽的列。
查看CPU和內存的使用率:
  在性能選項卡中谢肾,我們可以查看CPU和內存的使用率腕侄,根據(jù)CPU使用記錄的監(jiān)視器的個數(shù)還能看出邏輯處理核心的個數(shù),如我的雙核四線程的計算機就有四個監(jiān)視器芦疏。

image

線程的生命周期

當線程的數(shù)量小于處理器的數(shù)量時冕杠,線程的并發(fā)是真正的并發(fā),不同的線程運行在不同的處理器上酸茴。但當線程的數(shù)量大于處理器的數(shù)量時分预,線程的并發(fā)會受到一些阻礙,此時并不是真正的并發(fā)薪捍,因為此時至少有一個處理器會運行多個線程笼痹。

在單個處理器運行多個線程時,并發(fā)是一種模擬出來的狀態(tài)酪穿。操作系統(tǒng)采用時間片輪轉的方式輪流執(zhí)行每一個線程〉矢桑現(xiàn)在,幾乎所有的現(xiàn)代操作系統(tǒng)采用的都是時間片輪轉的搶占式調度方式被济,如我們熟悉的Unix救赐、Linux、Windows及macOS等流行的操作系統(tǒng)溉潭。

我們知道線程是程序執(zhí)行的最小單位净响,也是任務執(zhí)行的最小單位。在早期只有進程的操作系統(tǒng)中喳瓣,進程有五種狀態(tài)馋贤,創(chuàng)建、就緒畏陕、運行配乓、阻塞(等待)、退出惠毁。早期的進程相當于現(xiàn)在的只有單個線程的進程犹芹,那么現(xiàn)在的多線程也有五種狀態(tài),現(xiàn)在的多線程的生命周期與早期進程的生命周期類似鞠绰。

image

進程在運行過程有三種狀態(tài):就緒腰埂、運行、阻塞蜈膨,創(chuàng)建和退出狀態(tài)描述的是進程的創(chuàng)建過程和退出過程屿笼。

  • 創(chuàng)建:進程正在創(chuàng)建牺荠,還不能運行。操作系統(tǒng)在創(chuàng)建進程時要進行的工作包括分配和建立進程控制塊表項驴一、建立資源表格并分配資源休雌、加載程序并建立地址空間;

  • 就緒:時間片已用完肝断,此線程被強制暫停杈曲,等待下一個屬于它的時間片到來;

  • 運行:此線程正在執(zhí)行胸懈,正在占用時間片担扑;

  • 阻塞:也叫等待狀態(tài),等待某一事件(如IO或另一個線程)執(zhí)行完箫荡;

  • 退出:進程已結束魁亦,所以也稱結束狀態(tài),釋放操作系統(tǒng)分配的資源羔挡。

image
  • 創(chuàng)建:一個新的線程被創(chuàng)建洁奈,等待該線程被調用執(zhí)行;

  • 就緒:時間片已用完绞灼,此線程被強制暫停利术,等待下一個屬于它的時間片到來;

  • 運行:此線程正在執(zhí)行低矮,正在占用時間片印叁;

  • 阻塞:也叫等待狀態(tài),等待某一事件(如IO或另一個線程)執(zhí)行完军掂;

  • 退出:一個線程完成任務或者其他終止條件發(fā)生轮蜕,該線程終止進入退出狀態(tài),退出狀態(tài)釋放該線程所分配的資源蝗锥。

協(xié)程

協(xié)程跃洛,英文Coroutines,是一種基于線程之上终议,但又比線程更加輕量級的存在汇竭,這種由程序員自己寫程序來管理的輕量級線程叫做『用戶空間線程』,具有對內核來說不可見的特性穴张。

因為是自主開辟的異步任務细燎,所以很多人也更喜歡叫它們纖程(Fiber),或者綠色線程(GreenThread)皂甘。正如一個進程可以擁有多個線程一樣玻驻,一個線程也可以擁有多個協(xié)程。

image

協(xié)程的目的

在傳統(tǒng)的J2EE系統(tǒng)中都是基于每個請求占用一個線程去完成完整的業(yè)務邏輯(包括事務)偿枕。所以系統(tǒng)的吞吐能力取決于每個線程的操作耗時击狮。如果遇到很耗時的I/O行為佛析,則整個系統(tǒng)的吞吐立刻下降,因為這個時候線程一直處于阻塞狀態(tài)彪蓬,如果線程很多的時候,會存在很多線程處于空閑狀態(tài)(等待該線程執(zhí)行完才能執(zhí)行)捺萌,造成了資源應用不徹底档冬。

最常見的例子就是JDBC(它是同步阻塞的),這也是為什么很多人都說數(shù)據(jù)庫是瓶頸的原因桃纯。這里的耗時其實是讓CPU一直在等待I/O返回酷誓,說白了線程根本沒有利用CPU去做運算,而是處于空轉狀態(tài)态坦。而另外過多的線程盐数,也會帶來更多的ContextSwitch開銷。

對于上述問題伞梯,現(xiàn)階段行業(yè)里的比較流行的解決方案之一就是單線程加上異步回調玫氢。其代表派是node.js以及Java里的新秀Vert.x。

而協(xié)程的目的就是當出現(xiàn)長時間的I/O操作時谜诫,通過讓出目前的協(xié)程調度漾峡,執(zhí)行下一個任務的方式,來消除ContextSwitch上的開銷喻旷。

協(xié)程的特點

  1. 線程的切換由操作系統(tǒng)負責調度生逸,協(xié)程由用戶自己進行調度,因此減少了上下文切換且预,提高了效率槽袄。
  2. 線程的默認Stack大小是1M,而協(xié)程更輕量锋谐,接近1K遍尺。因此可以在相同的內存中開啟更多的協(xié)程。
  3. 由于在同一個線程上怀估,因此可以避免競爭關系而使用鎖狮鸭。
  4. 適用于被阻塞的,且需要大量并發(fā)的場景多搀。但不適用于大量計算的多線程歧蕉,遇到此種情況,更好實用線程去解決康铭。

協(xié)程的原理

當出現(xiàn)IO阻塞的時候惯退,由協(xié)程的調度器進行調度,通過將數(shù)據(jù)流立刻yield掉(主動讓出)从藤,并且記錄當前棧上的數(shù)據(jù)催跪,阻塞完后立刻再通過線程恢復棧锁蠕,并把阻塞的結果放到這個線程上去跑,這樣看上去好像跟寫同步代碼沒有任何差別懊蒸,這整個流程可以稱為coroutine荣倾,而跑在由coroutine負責調度的線程稱為Fiber。比如Golang里的 go關鍵字其實就是負責開啟一個Fiber骑丸,讓func邏輯跑在上面舌仍。

由于協(xié)程的暫停完全由程序控制,發(fā)生在用戶態(tài)上通危;而線程的阻塞狀態(tài)是由操作系統(tǒng)內核來進行切換铸豁,發(fā)生在內核態(tài)上。
因此菊碟,協(xié)程的開銷遠遠小于線程的開銷节芥,也就沒有了ContextSwitch上的開銷。

協(xié)程和線程的比較

比較項 線程 協(xié)程
占用資源 初始單位為1MB,固定不可變 初始一般為 2KB逆害,可隨需要而增大
調度所屬 由 OS 的內核完成 由用戶完成
切換開銷 涉及模式切換(從用戶態(tài)切換到內核態(tài))头镊、16個寄存器、PC忍燥、SP...等寄存器的刷新等 只有三個寄存器的值修改 - PC / SP / DX.
性能問題 資源占用太高拧晕,頻繁創(chuàng)建銷毀會帶來嚴重的性能問題 資源占用小,不會帶來嚴重的性能問題
數(shù)據(jù)同步 需要用鎖等機制確保數(shù)據(jù)的一直性和可見性 不需要多線程的鎖機制,因為只有一個線程梅垄,也不存在同時寫變量沖突厂捞,在協(xié)程中控制共享資源不加鎖,只需要判斷狀態(tài)就好了队丝,所以執(zhí)行效率比多線程高很多靡馁。

作者:一入碼坑深似海
鏈接:http://www.reibang.com/p/80bde972196d
來源:簡書
著作權歸作者所有。商業(yè)轉載請聯(lián)系作者獲得授權机久,非商業(yè)轉載請注明出處臭墨。
](//upload-images.jianshu.io/upload_images/17179731-f928e23ab6a3f993.png?imageMogr2/auto-orient/strip|imageView2/2/w/726/format/webp)

如上圖,在任務管理器的進程一欄里膘盖,有道詞典和有道云筆記就是進程胧弛,而在進程下又有著多個執(zhí)行不同任務的線程。

任務調度

線程是什么侠畔?要理解這個概念结缚,需要先了解一下操作系統(tǒng)的一些相關概念。大部分操作系統(tǒng)(如Windows软棺、Linux)的任務調度是采用時間片輪轉的搶占式調度方式红竭。

在一個進程中,當一個線程任務執(zhí)行幾毫秒后,會由操作系統(tǒng)的內核(負責管理各個任務)進行調度茵宪,通過硬件的計數(shù)器中斷處理器最冰,讓該線程強制暫停并將該線程的寄存器放入內存中,通過查看線程列表決定接下來執(zhí)行哪一個線程稀火,并從內存中恢復該線程的寄存器暖哨,最后恢復該線程的執(zhí)行,從而去執(zhí)行下一個任務憾股。
上述過程中鹿蜀,任務執(zhí)行的那一小段時間叫做時間片,任務正在執(zhí)行時的狀態(tài)叫運行狀態(tài)服球,被暫停的線程任務狀態(tài)叫做就緒狀態(tài),意為等待下一個屬于它的時間片的到來颠焦。

這種方式保證了每個線程輪流執(zhí)行斩熊,由于CPU的執(zhí)行效率非常高,時間片非常短伐庭,在各個任務之間快速地切換粉渠,給人的感覺就是多個任務在“同時進行”,這也就是我們所說的并發(fā)(別覺得并發(fā)有多高深圾另,它的實現(xiàn)很復雜霸株,但它的概念很簡單,就是一句話:多個任務同時執(zhí)行)集乔。多任務運行過程的示意圖如下:

image

進程與線程的區(qū)別

前面講了進程與線程,但可能你還覺得迷糊扰路,感覺他們很類似汗唱。的確哩罪,進程與線程有著千絲萬縷的關系,下面就讓我們一起來理一理:

  1. 線程是程序執(zhí)行的最小單位碘耳,而進程是操作系統(tǒng)分配資源的最小單位藏畅;
  2. 一個進程由一個或多個線程組成愉阎,線程是一個進程中代碼的不同執(zhí)行路線;
  3. 進程之間相互獨立幽七,但同一進程下的各個線程之間共享程序的內存空間(包括代碼段澡屡、數(shù)據(jù)集咐旧、堆等)及一些進程級的資源(如打開文件和信號),某進程內的線程在其它進程不可見室埋;
  4. 調度和切換:線程上下文切換比進程上下文切換要快得多伊约。

線程與進程關系的示意圖:

image
image

總之屡律,線程和進程都是一種抽象的概念,線程是一種比進程更小的抽象搏讶,線程和進程都可用于實現(xiàn)并發(fā)窍蓝。
在早期的操作系統(tǒng)中并沒有線程的概念繁成,進程是能擁有資源和獨立運行的最小單位巾腕,也是程序執(zhí)行的最小單位尊搬。它相當于一個進程里只有一個線程佛寿,進程本身就是線程。所以線程有時被稱為輕量級進程(Lightweight Process蜡饵,LWP)胳施。

image

后來舞肆,隨著計算機的發(fā)展焦辅,對多個任務之間上下文切換的效率要求越來越高,就抽象出一個更小的概念——線程筷登,一般一個進程會有多個(也可是一個)線程。

image

多線程與多核

上面提到的時間片輪轉的調度方式說一個任務執(zhí)行一小段時間后強制暫停去執(zhí)行下一個任務仆抵,每個任務輪流執(zhí)行。很多操作系統(tǒng)的書都說“同一時間點只有一個任務在執(zhí)行”种冬。那有人可能就要問雙核處理器呢舔糖?難道兩個核不是同時運行嗎娱两?

其實“同一時間點只有一個任務在執(zhí)行”這句話是不準確的,至少它是不全面的金吗。那多核處理器的情況下,線程是怎樣執(zhí)行呢旱物?這就需要了解內核線程卫袒。

多核(心)處理器是指在一個處理器上集成多個運算核心從而提高計算能力,也就是有多個真正并行計算的處理核心码秉,每一個處理核心對應一個內核線程逮矛。
內核線程(Kernel Thread,KLT)就是直接由操作系統(tǒng)內核支持的線程,這種線程由內核來完成線程切換晋控,內核通過操作調度器對線程進行調度汞窗,并負責將線程的任務映射到各個處理器上。一般一個處理核心對應一個內核線程糖荒,比如單核處理器對應一個內核線程杉辙,雙核處理器對應兩個內核線程,四核處理器對應四個內核線程捶朵。

現(xiàn)在的電腦一般是雙核四線程蜘矢、四核八線程,是采用超線程技術將一個物理處理核心模擬成兩個邏輯處理核心综看,對應兩個內核線程品腹,所以在操作系統(tǒng)中看到的CPU數(shù)量是實際物理CPU數(shù)量的兩倍,如你的電腦是雙核四線程红碑,打開“任務管理器\性能”可以看到4個CPU的監(jiān)視器舞吭,四核八線程可以看到8個CPU的監(jiān)視器。

image

超線程技術就是利用特殊的硬件指令析珊,把一個物理芯片模擬成兩個邏輯處理核心羡鸥,讓單個處理器都能使用線程級并行計算,進而兼容多線程操作系統(tǒng)和軟件忠寻,減少了CPU的閑置時間惧浴,提高的CPU的運行效率。這種超線程技術(如雙核四線程)由處理器硬件的決定奕剃,同時也需要操作系統(tǒng)的支持才能在計算機中表現(xiàn)出來衷旅。

程序一般不會直接去使用內核線程,而是去使用內核線程的一種高級接口——輕量級進程(Lightweight Process纵朋,LWP)柿顶,輕量級進程就是我們通常意義上所講的線程,也被叫做用戶線程操软。由于每個輕量級進程都由一個內核線程支持嘁锯,因此只有先支持內核線程,才能有輕量級進程寺鸥。用戶線程與內核線程的對應關系有三種模型:一對一模型猪钮、多對一模型、多對多模型胆建,在這以4個內核線程烤低、3個用戶線程為例對三種模型進行說明。

一對一模型

對于一對一模型來說笆载,一個用戶線程就唯一地對應一個內核線程(反過來不一定成立扑馁,一個內核線程不一定有對應的用戶線程)涯呻。這樣,如果CPU沒有采用超線程技術(如四核四線程的計算機)腻要,一個用戶線程就唯一地映射到一個物理CPU的內核線程复罐,線程之間的并發(fā)是真正的并發(fā)。一對一模型使用戶線程具有與內核線程一樣的優(yōu)點雄家,一個線程因某種原因阻塞時其他線程的執(zhí)行不受影響效诅;此處,一對一模型也可以讓多線程程序在多處理器的系統(tǒng)上有更好的表現(xiàn)趟济。

但一對一模型也有兩個缺點:

  1. 許多操作系統(tǒng)限制了內核線程的數(shù)量乱投,因此一對一模型會使用戶線程的數(shù)量受到限制;
  2. 許多操作系統(tǒng)內核線程調度時顷编,上下文切換的開銷較大戚炫,導致用戶線程的執(zhí)行效率下降。
image

多對一模型

多對一模型將多個用戶線程映射到一個內核線程上媳纬,線程之間的切換由用戶態(tài)的代碼來進行双肤,系統(tǒng)內核感受不到線程的實現(xiàn)方式。用戶線程的建立钮惠、同步茅糜、銷毀等都在用戶態(tài)中完成,不需要內核的介入素挽。因此相對一對一模型限匣,多對一模型的線程上下文切換速度要快許多;此外毁菱,多對一模型對用戶線程的數(shù)量幾乎無限制。

但多對一模型也有兩個缺點:

  1. 如果其中一個用戶線程阻塞锌历,那么其它所有線程都將無法執(zhí)行贮庞,因為此時內核線程也隨之阻塞了;
  2. 在多處理器系統(tǒng)上究西,處理器數(shù)量的增加對多對一模型的線程性能不會有明顯的增加窗慎,因為所有的用戶線程都映射到一個處理器上了。
image

多對多模型

多對多模型結合了一對一模型和多對一模型的優(yōu)點卤材,將多個用戶線程映射到多個內核線程上遮斥。由線程庫負責在可用的可調度實體上調度用戶線程,這使得線程的上下文切換非成却裕快术吗,因為它避免了系統(tǒng)調用。但是增加了復雜性和優(yōu)先級倒置的可能性帆精,以及在用戶態(tài)調度程序和內核調度程序之間沒有廣泛(且高昂)協(xié)調的次優(yōu)調度较屿。

多對多模型的優(yōu)點有:

  1. 一個用戶線程的阻塞不會導致所有線程的阻塞隧魄,因為此時還有別的內核線程被調度來執(zhí)行;
  2. 多對多模型對用戶線程的數(shù)量沒有限制隘蝎;
  3. 在多處理器的操作系統(tǒng)中购啄,多對多模型的線程也能得到一定的性能提升,但提升的幅度不如一對一模型的高嘱么。
image

在現(xiàn)在流行的操作系統(tǒng)中狮含,大都采用多對多的模型。

查看進程與線程

一個應用程序可能是多線程的曼振,也可能是多進程的几迄,如何查看呢?在Windows下我們只須打開任務管理器就能查看一個應用程序的進程和線程數(shù)拴测。按“Ctrl+Alt+Del”或右鍵快捷工具欄打開任務管理器乓旗。

查看進程數(shù)和線程數(shù):

image

在“進程”選項卡下,我們可以看到一個應用程序包含的線程數(shù)集索。如果一個應用程序有多個進程屿愚,我們能看到每一個進程,如在上圖中务荆,Google的Chrome瀏覽器就有多個進程妆距。同時,如果打開了一個應用程序的多個實例也會有多個進程函匕,如上圖中我打開了兩個cmd窗口娱据,就有兩個cmd進程。如果看不到線程數(shù)這一列盅惜,可以再點擊“查看\選擇列”菜單中剩,增加監(jiān)聽的列。
查看CPU和內存的使用率:
  在性能選項卡中抒寂,我們可以查看CPU和內存的使用率结啼,根據(jù)CPU使用記錄的監(jiān)視器的個數(shù)還能看出邏輯處理核心的個數(shù),如我的雙核四線程的計算機就有四個監(jiān)視器屈芜。

image

線程的生命周期

當線程的數(shù)量小于處理器的數(shù)量時郊愧,線程的并發(fā)是真正的并發(fā),不同的線程運行在不同的處理器上井佑。但當線程的數(shù)量大于處理器的數(shù)量時属铁,線程的并發(fā)會受到一些阻礙,此時并不是真正的并發(fā)躬翁,因為此時至少有一個處理器會運行多個線程焦蘑。

在單個處理器運行多個線程時,并發(fā)是一種模擬出來的狀態(tài)盒发。操作系統(tǒng)采用時間片輪轉的方式輪流執(zhí)行每一個線程±撸現(xiàn)在坟乾,幾乎所有的現(xiàn)代操作系統(tǒng)采用的都是時間片輪轉的搶占式調度方式,如我們熟悉的Unix蝶防、Linux甚侣、Windows及macOS等流行的操作系統(tǒng)。

我們知道線程是程序執(zhí)行的最小單位间学,也是任務執(zhí)行的最小單位殷费。在早期只有進程的操作系統(tǒng)中,進程有五種狀態(tài)低葫,創(chuàng)建详羡、就緒、運行嘿悬、阻塞(等待)实柠、退出。早期的進程相當于現(xiàn)在的只有單個線程的進程善涨,那么現(xiàn)在的多線程也有五種狀態(tài)窒盐,現(xiàn)在的多線程的生命周期與早期進程的生命周期類似。

image

進程在運行過程有三種狀態(tài):就緒钢拧、運行蟹漓、阻塞,創(chuàng)建和退出狀態(tài)描述的是進程的創(chuàng)建過程和退出過程源内。

  • 創(chuàng)建:進程正在創(chuàng)建葡粒,還不能運行。操作系統(tǒng)在創(chuàng)建進程時要進行的工作包括分配和建立進程控制塊表項膜钓、建立資源表格并分配資源嗽交、加載程序并建立地址空間;

  • 就緒:時間片已用完颂斜,此線程被強制暫停轮纫,等待下一個屬于它的時間片到來;

  • 運行:此線程正在執(zhí)行焚鲜,正在占用時間片;

  • 阻塞:也叫等待狀態(tài)放前,等待某一事件(如IO或另一個線程)執(zhí)行完忿磅;

  • 退出:進程已結束,所以也稱結束狀態(tài)凭语,釋放操作系統(tǒng)分配的資源葱她。

image
  • 創(chuàng)建:一個新的線程被創(chuàng)建杏愤,等待該線程被調用執(zhí)行链瓦;

  • 就緒:時間片已用完搪花,此線程被強制暫停刁愿,等待下一個屬于它的時間片到來;

  • 運行:此線程正在執(zhí)行豪墅,正在占用時間片泉手;

  • 阻塞:也叫等待狀態(tài),等待某一事件(如IO或另一個線程)執(zhí)行完偶器;

  • 退出:一個線程完成任務或者其他終止條件發(fā)生斩萌,該線程終止進入退出狀態(tài),退出狀態(tài)釋放該線程所分配的資源屏轰。

協(xié)程

協(xié)程颊郎,英文Coroutines,是一種基于線程之上霎苗,但又比線程更加輕量級的存在姆吭,這種由程序員自己寫程序來管理的輕量級線程叫做『用戶空間線程』,具有對內核來說不可見的特性唁盏。

因為是自主開辟的異步任務内狸,所以很多人也更喜歡叫它們纖程(Fiber),或者綠色線程(GreenThread)升敲。正如一個進程可以擁有多個線程一樣答倡,一個線程也可以擁有多個協(xié)程。

image

協(xié)程的目的

在傳統(tǒng)的J2EE系統(tǒng)中都是基于每個請求占用一個線程去完成完整的業(yè)務邏輯(包括事務)驴党。所以系統(tǒng)的吞吐能力取決于每個線程的操作耗時瘪撇。如果遇到很耗時的I/O行為,則整個系統(tǒng)的吞吐立刻下降港庄,因為這個時候線程一直處于阻塞狀態(tài)倔既,如果線程很多的時候,會存在很多線程處于空閑狀態(tài)(等待該線程執(zhí)行完才能執(zhí)行)鹏氧,造成了資源應用不徹底渤涌。

最常見的例子就是JDBC(它是同步阻塞的),這也是為什么很多人都說數(shù)據(jù)庫是瓶頸的原因把还。這里的耗時其實是讓CPU一直在等待I/O返回实蓬,說白了線程根本沒有利用CPU去做運算,而是處于空轉狀態(tài)吊履。而另外過多的線程安皱,也會帶來更多的ContextSwitch開銷。

對于上述問題艇炎,現(xiàn)階段行業(yè)里的比較流行的解決方案之一就是單線程加上異步回調酌伊。其代表派是node.js以及Java里的新秀Vert.x。

而協(xié)程的目的就是當出現(xiàn)長時間的I/O操作時缀踪,通過讓出目前的協(xié)程調度居砖,執(zhí)行下一個任務的方式虹脯,來消除ContextSwitch上的開銷。

協(xié)程的特點

  1. 線程的切換由操作系統(tǒng)負責調度奏候,協(xié)程由用戶自己進行調度循集,因此減少了上下文切換,提高了效率鼻由。
  2. 線程的默認Stack大小是1M暇榴,而協(xié)程更輕量,接近1K蕉世。因此可以在相同的內存中開啟更多的協(xié)程蔼紧。
  3. 由于在同一個線程上,因此可以避免競爭關系而使用鎖狠轻。
  4. 適用于被阻塞的奸例,且需要大量并發(fā)的場景。但不適用于大量計算的多線程向楼,遇到此種情況查吊,更好實用線程去解決。

協(xié)程的原理

當出現(xiàn)IO阻塞的時候湖蜕,由協(xié)程的調度器進行調度逻卖,通過將數(shù)據(jù)流立刻yield掉(主動讓出),并且記錄當前棧上的數(shù)據(jù)昭抒,阻塞完后立刻再通過線程恢復棧评也,并把阻塞的結果放到這個線程上去跑,這樣看上去好像跟寫同步代碼沒有任何差別灭返,這整個流程可以稱為coroutine盗迟,而跑在由coroutine負責調度的線程稱為Fiber。比如Golang里的 go關鍵字其實就是負責開啟一個Fiber熙含,讓func邏輯跑在上面罚缕。

由于協(xié)程的暫停完全由程序控制,發(fā)生在用戶態(tài)上怎静;而線程的阻塞狀態(tài)是由操作系統(tǒng)內核來進行切換邮弹,發(fā)生在內核態(tài)上。
因此蚓聘,協(xié)程的開銷遠遠小于線程的開銷腌乡,也就沒有了ContextSwitch上的開銷。

協(xié)程和線程的比較

比較項 線程 協(xié)程
占用資源 初始單位為1MB,固定不可變 初始一般為 2KB或粮,可隨需要而增大
調度所屬 由 OS 的內核完成 由用戶完成
切換開銷 涉及模式切換(從用戶態(tài)切換到內核態(tài))、16個寄存器捞高、PC氯材、SP...等寄存器的刷新等 只有三個寄存器的值修改 - PC / SP / DX.
性能問題 資源占用太高渣锦,頻繁創(chuàng)建銷毀會帶來嚴重的性能問題 資源占用小,不會帶來嚴重的性能問題
數(shù)據(jù)同步 需要用鎖等機制確保數(shù)據(jù)的一直性和可見性 不需要多線程的鎖機制,因為只有一個線程氢哮,也不存在同時寫變量沖突袋毙,在協(xié)程中控制共享資源不加鎖,只需要判斷狀態(tài)就好了冗尤,所以執(zhí)行效率比多線程高很多听盖。

本文內容為轉載,版權歸作者所有

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末裂七,一起剝皮案震驚了整個濱河市皆看,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌背零,老刑警劉巖腰吟,帶你破解...
    沈念sama閱讀 218,284評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異徙瓶,居然都是意外死亡毛雇,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評論 3 395
  • 文/潘曉璐 我一進店門侦镇,熙熙樓的掌柜王于貴愁眉苦臉地迎上來灵疮,“玉大人,你說我怎么就攤上這事壳繁≌鸬罚” “怎么了?”我有些...
    開封第一講書人閱讀 164,614評論 0 354
  • 文/不壞的土叔 我叫張陵氮趋,是天一觀的道長伍派。 經常有香客問我,道長剩胁,這世上最難降的妖魔是什么诉植? 我笑而不...
    開封第一講書人閱讀 58,671評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮昵观,結果婚禮上晾腔,老公的妹妹穿的比我還像新娘。我一直安慰自己啊犬,他們只是感情好灼擂,可當我...
    茶點故事閱讀 67,699評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著觉至,像睡著了一般剔应。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,562評論 1 305
  • 那天峻贮,我揣著相機與錄音席怪,去河邊找鬼。 笑死纤控,一個胖子當著我的面吹牛挂捻,可吹牛的內容都是我干的。 我是一名探鬼主播船万,決...
    沈念sama閱讀 40,309評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼刻撒,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了耿导?” 一聲冷哼從身側響起声怔,我...
    開封第一講書人閱讀 39,223評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎碎节,沒想到半個月后捧搞,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 45,668評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡狮荔,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,859評論 3 336
  • 正文 我和宋清朗相戀三年胎撇,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片殖氏。...
    茶點故事閱讀 39,981評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡晚树,死狀恐怖,靈堂內的尸體忽然破棺而出雅采,到底是詐尸還是另有隱情爵憎,我是刑警寧澤,帶...
    沈念sama閱讀 35,705評論 5 347
  • 正文 年R本政府宣布婚瓜,位于F島的核電站宝鼓,受9級特大地震影響,放射性物質發(fā)生泄漏巴刻。R本人自食惡果不足惜愚铡,卻給世界環(huán)境...
    茶點故事閱讀 41,310評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望胡陪。 院中可真熱鬧沥寥,春花似錦、人聲如沸柠座。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,904評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽妈经。三九已至淮野,卻和暖如春捧书,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背骤星。 一陣腳步聲響...
    開封第一講書人閱讀 33,023評論 1 270
  • 我被黑心中介騙來泰國打工鳄厌, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人妈踊。 一個月前我還...
    沈念sama閱讀 48,146評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像泪漂,于是被迫代替她去往敵國和親廊营。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,933評論 2 355

推薦閱讀更多精彩內容