淺談進程、線程和協程三者之間的區別和聯繫

1、進程、線程、協程

1,進程

經典定義:一個執行中程序的實例。系統中的每一個程序都運行在某個進程的上下文中。(-摘自 CSAPP)html

進程是系統資源分配的最小單位python

 

2,線程(thread)

線程就是運行在進程上下文中的邏輯流。golang

線程是操做系統可以進行運算調度的最小單位。服務器

 

3,協程

相對子例程而言,協程更爲通常和靈活,但在實踐中使用沒有子例程那樣普遍。併發

根據維基百科對子例程的描述:是一個大型程序中的某部分代碼,由一個或多個語句塊組成。它負責完成某項特定任務,並且相較於其餘代碼,具有相對的獨立性。我能夠將子例程理解爲一個函數。函數

 

4,區別和聯繫

首先,進程提供給應用程序的關鍵抽象爲:高併發

  • 一個獨立的邏輯控制流:它提供一個假象,好像咱們的程序獨佔地使用處理器。
  • 一個私有的地址空間,它提供一個假象,好像咱們的程序獨佔地使用內存系統。

從以上描述我能夠看出,一個進程是一個獨立進行的任務,它佔用的系統資源有:地址空間,全局變量,文件描述符,硬件資源等。性能

 

進程出現的目的,是爲了更好的利用CPU資源。例如:lua

假設有兩個任務A和B,當A遇到IO操做,CPU默默的等待任務A讀取完操做再去執行任務B,這樣無疑是對CPU資源的極大的浪費。若在任務A讀取數據時,讓任務B執行,當任務A讀取完數據後,再切換到任務A執行,這樣就能夠更好地利用CPU資源。這裏的切換涉及到狀態的保存,狀態的恢復,須要有一個東西去記錄任務A和任務B分別須要什麼資源,怎樣去識別任務A和任務B,這時進程就出現了。操作系統

所以,經過進程來分配系統資源,標識任務。

如何分配CPU去執行進程稱之爲調度,進程狀態的記錄,恢復,上下文切換(簡稱切換)。

 

其次,若上面說起的任務A是一個文本程序,須要接受鍵盤輸入,將內容顯示在屏幕上,還須要保存信息到硬盤中。

若只有一個進程,會形成同一時間只能幹同樣事的尷尬(當保存時,就不能經過鍵盤輸入內容)。如有多個進程,每一個進程負責一個任務,進程A負責接收鍵盤輸入的任務,進程B負責將內容顯示在屏幕上的任務,進程C負責保存內容到硬盤中的任務。這裏進程A,B,C間的協做涉及到了進程通訊問題,並且有共同都須要擁有的東西-------文本內容,不停的切換形成性能上的損失。如有一種機制,可使任務A,B,C共享資源,這樣上下文切換所須要保存和恢復的內容就少了,同時又能夠減小通訊所帶來的性能損耗,那就行了。這時線程出現了。
所以,線程共享進程的大部分資源,並參與CPU的調度。
 
 
假設當涉及到大規模的併發請求鏈接時,例若有一萬我的同時鏈接個人服務器,但系統資源有限,若是以線程做爲處理單元,調內部系統資源的話大部分線程都處於等待狀態,但用了協程就能夠實現線程本身調度,不陷入內核級別的上下文切換。這時協程出現了。
所以,協程經過在線程中實現調度,避免了陷入內核級別的上下文切換形成的性能損失,進而突破了線程在IO上的性能瓶頸。
 
爲何協程不須要通過內核級別的上下文切換,我是這樣認爲的:
進程和線程都是操做系統自帶的,協程是有些程序原生支持的,例如go,lua, 有些是後期版本纔有的,好比python2.5 C#等。
 
 

2、小結

以前作遊戲服務器時就對這塊概念不是很清晰,如今作流媒體服務時又碰到了這樣的困惑,所以專門看書思考整理了相關的知識點,網上也參考了許多例子。若是有錯誤還望及時指正。
在此引用一位從事服務器開發的前輩說的話:
核心只有一個, 線程是操做系統調度,協程是用戶態調度。
協程沒必要須是語言集成,例如C語言能夠用setjmp/longjmp實現,也能夠本身經過改變esp指針換棧實現協程。
協程自己跟高吞吐沒任何關係,基於io多路複用+回調就能夠實現高併發和高吞吐。 引入協程是爲了將回調邏輯變成線性同步邏輯。
 

參考資料

維基百科_進程

維基百科_線程

維基百科_協程

簡書_進程,線程,協程與並行,併發

進程、線程、輕量級進程、協程與 go 的 goroutine【轉載+整理】

博客園_進程和線程、協程的區別

相關文章
相關標籤/搜索