進程、線程、協程、例程、過程的區別是什麼?

引自我在知乎上的回答:進程 線程 協程 例程 過程 的區別是什麼? - 駿馬金龍的回答 - 知乎shell

首先解釋下程序、進程、上下文切換和線程。而後再解釋協程、例程、過程。編程

程序

程序:源代碼堆起來的東西。至關於一個一動不動沒有生命的機器人。bash

  • 雖然是沒有生命的機器人,可是它被設計後就表示有了硬件,它的硬件決定了以後它有生命後是如何幹活的
  • 機器人有優劣,因此有些優秀的機器人幹活很快,而有些機器人幹活很慢

進程

進程:程序在系統上跑起來(運行)以後的東西(動態的)。至關於有了生命的機器人。生命是內核給的,動起來的能力是CPU提供的驅動力。多線程

  • 由於在操做系統看來,它已經有了生命,會賦予它一些屬性,好比它叫什麼(PID),誰發明的(PPID),它在哪一個範圍內活動(地址空間).................
  • 內核會記錄這個機器人的信息
  • 機器人能夠造機器人(父子進程)
  • 它能夠中斷或睡眠。好比機器人想充電,可是沒電給它,它得等
  • 內核能夠跟它交流,好比傳遞一些數據給它、傳遞信號給它
  • 它能夠被毀掉。好比進程崩潰或正常退出或被信號終止
  • 機器人毀掉後要爲它收屍,若是機器人偷偷死掉,就會沒人給它收屍而變成殭屍進程
  • 嚴格地說,這個機器人運行起來以後雖然有了生命,可是沒有靈魂,只有CPU給它驅動力的那一段時間,他才能動起來,其它時候都是在哪裏睡覺
  • .......

上下文切換

上下文切換:在某一時刻,一核CPU只能執行一個進程。至關於內核只能讓一核CPU爲一個機器人提供驅動力讓它動起來(1:1),多核CPU能夠同時爲多個機器人提供驅動力。併發

  • 可是操做系統上有不少機器人在幹活,因此內核要控制CPU不斷的爲不一樣機器人來回提供驅動力,這是進程切換(這是站在內核的角度上看的,也叫上下文切換)
  • 爲了讓你感受機器人沒有中止工做,內核控制只給每一個機器人一點點的CPU時間片。這就至關於轉起來的電扇,你感受沒有間隙,實際上是有的,只是間隙時間過短,人眼難辨。如今能夠腦部一下,一大堆的機器人在你面前徹底不停的跳着鬼步舞....
  • 由於CPU要切換給不一樣的機器人提供驅動力,因此每次切換以前的機器人幹活到了哪裏以及它的狀態得記錄下來,這是上下文保留(保護現場)
  • 保護現場是必要的額外的工做,頻繁上下文切換會浪費CPU在這些額外工做上
  • .........

線程

線程:一個進程內能夠有多個執行線程,或者說線程是進程內部的小型進程。至關於在機器人內部根據機器人自身克隆了不少個基本徹底相同的體內小機器人。編程語言

  • 進程內部能夠有多個線程同時執行
  • 進程內的全部線程擁有的源代碼徹底相同。只是有些小型機器人執行任務A,有些小型機器人執行任務B。而這些任務本來是應該被那個大機器人完成的
  • 全部小型機器人活動範圍相同,即某進程內全部線程都共享地址空間。可是每一個小型機器人也得有本身的一點私人空間(線程有本身的棧空間)
  • 小型機器人共享不少屬性(都來源於大機器人),也有不少本身的屬性
  • 線程也要切換。只是線程切換須要作的額外工做要比進程切換少的多

如今,對比一下多進程和多線程?函數

函數

而後是協程、例程、過程。可是在解釋這個以前,先解釋下如今更爲俗知的函數,以及它們和進程、線程之間的關係。oop

函數:一種代碼段。用來表示一個要完成的任務單元。固然,這個任務裏可能也包含了其它多個子任務。操作系統

再說程序

  • 程序的主體部分是函數,包括一個程序的入口函數(main函數,有些語言(一半是動態語言)不要求你敲main函數的聲明,這些語言會在程序執行的時候默默給你加上main)以及其它一些本身編寫的函數
  • 除了函數外,程序中可能還有一些全局屬性的定義、一些額外的屬於編程語言自身的額外代碼,好比說程序文件頭部可能聲明這是一個包以及要導入什麼包之類的
  • 函數是要被進程(或線程)執行的,機器人幹什麼的?就是執行函數的。程序的運行從執行入口函數main開始,而後在main中調用其它你本身編寫的函數,也就是說跳轉到其它函數上去執行一個個的任務。畫重點,函數是要被執行的,函數的執行是能夠進行跳轉的
  • 那些非函數代碼(好比全局屬性的定義代碼、導包代碼),是在程序被裝載準備執行的時候完成好的工做。

先總結一下重點:進程、線程是機器人,函數是機器人要乾的活線程

函數怎麼執行的

機器人是怎麼執行函數的呢?在不使用coroutine(也就是國人翻譯後的"協程")的狀況下,它的執行流程是固定的:從main函數開始,跳轉執行其它函數A,main函數被擱置等待,它必須等待跳轉後的函數A執行完返回後才能回到main函數繼續向下執行,若是函數A中還有調用函數B,那麼函數A被擱置等待,它必須等待函數B執行完返回後才繼續向下執行。直到main函數也執行完,程序退出。

繼續用機器人來比喻,那就是機器人要執行一個任務,但這個任務中要求臨時去執行另外一個任務,那麼這個機器人必須先去執行另外一個任務,並只能在執行完另外一個任務的時候回到以前的那個任務繼續執行。

因此,重點是:函數A中在第X行開始跳轉調用函數B時,函數A必須等待函數B執行完返回後才能繼續從第X行處開始繼續向下

協程、例程、過程

回到正題要解釋的東西:協程、例程、過程。

很遺憾,例程、過程、子程序,它們都是函數的不一樣稱呼,不一樣的時代、不一樣的編程語言稱呼的方式不同,都是任務單元。甚至面向對象裏的方法也是函數,只不過在面向對象的面具之下,它有一些和麪向對象相關的特性。

最後是協程。我猜你說的協程應該是coroutine這類東西,全稱是cooperative routine,也叫作cooperative tasks。只看英文的話,意思已經很明顯了:協同運行的子程序(子程序就是例程、函數、過程)。或者說是協同執行的任務。

在解釋coroutine以前,多插一句嘴。
coroutine被翻譯爲協程,我的認爲是不太合理的。在shell中有coproc的概念(ksh/bash等一些shell都支持coproc的功能),cooperative process,表示協同運行的進程,按單詞字面意思,這才應該被翻譯爲協程。

協同運行?這是個什麼意思。回顧下剛纔解釋函數(由於是co routine,因此我下面所有用routine來替換函數的說法)的執行流程在理解協同運行是什麼意思。

在正常狀況下,routine跳轉運行後必須原地等待跳轉後的那個routine執行完返回才能繼續從原地向下執行。

可是,使用coroutine的時候,假設routine1和coroutine2互爲coroutine,那麼routine1跳轉到routine2去執行的時候,它會等待routine2才能繼續向下執行,可是不必定是等待routine2執行完,也多是等待routine2從新跳轉回routine1(由於routine2已經產生了一些可讓routine1繼續運行的數據,而不是讓routine1繼續在那裏阻塞)。同理routine2也可能會原地等待routine1,routine1再跳回routine2。

可是這怎麼行,來來回回的跳轉總得退出吧?這就是跟所寫的代碼有關係了。好比某個routine中加入一個判斷,達到某一條件時就再也不跳轉,而是直接向下執行。

機器人的比喻又來了。機器人要執行一個任務,但要求臨時去執行另外一個任務,如今不強制規定必須執行完另外一個任務纔回來執行原始任務,而是能夠在執行另外一個任務的時候,又臨時回來執行原始任務。

若是說很差理解,那麼下面這個wiki中給的生產者消費者模型的僞代碼很容易幫助理解coroutine,這個程序不必定合理,但很是適合於理解"協同"的意義。其中q是一個隊列,生產者coroutine會跳轉到消費者coroutine,同理消費者coroutine也同樣會跳轉到生產者coroutine。

var q := new queue

coroutine produce
    loop
        while q is not full
            create some new items
            add the items to q
        yield to consume

coroutine consume
    loop
        while q is not empty
            remove some items from q
            use the items
        yield to produce

這就是協同運行以及coroutine的解釋,我想應該已經不難理解了。

coroutine的優勢

最後,說一下coroutine的優勢:

實際上,coroutine能夠認爲是單線程多任務的工做方式(固然,進程中實現coroutine也是能夠的),由於它在單個線程中的多個任務之間直接跳轉,而多線程是經過上下文切換來實現多任務的。換句話說,coroutine提供了併發卻不併行的功能。經過coroutine,還能實現更爲"實時"的上下文任務,由於coroutine之間的跳轉切換不須要任何系統調用和可能的阻塞調用,不須要像多線程同樣爲了線程之間的資源同步而使用額外的互斥鎖、信號量等。

相關文章
相關標籤/搜索