1. 程式人生 > 其它 >程序、執行緒和協程有什麼區別

程序、執行緒和協程有什麼區別

什麼是程序和執行緒

有一定基礎的小夥伴們肯定都知道程序和執行緒。

   

程序是什麼呢?

直白地講,程序就是應用程式的啟動例項。比如我們執行一個遊戲,開啟一個軟體,就是開啟了一個程序。

程序擁有程式碼和開啟的檔案資源、資料資源、獨立的記憶體空間

有人會認為,要提升CPU的利用率,可以開多個程序,但是開多個程序的話,程序間通訊是個比較麻煩的事情(程序之間地址空間是獨立的,需要通過其他方式,例如:管道來解決)

相反,執行緒之間是可以實現資料共享的,因為執行緒之間使用的是同一個地址空間。

執行緒又是什麼呢?

執行緒又叫做輕量級程序,是CPU排程的最小單位,執行緒從屬於程序,是程式的實際執行者。

一個程序至少包含一個主執行緒,也可以有更多的子執行緒。

多個執行緒共享所屬程序的資源,同時執行緒也擁有自己的專屬資源、擁有自己的棧空間

程間通訊主要通過共享記憶體,上下文切換很快,資源開銷較少,但相比程序不夠穩定容易丟失資料

執行緒和程序一樣都是由作業系統的排程器來統一排程,執行緒本身的資料結構需要佔用記憶體,頻繁建立和銷燬執行緒會加大系統的壓力。另外,執行緒太多,系統排程的開銷也會增大。

基於上面的情況,提出了執行緒池解決方案,在初始化的時候批量建立執行緒,然後使用者後續通過佇列等方式提交業務邏輯,執行緒池中的執行緒進行邏輯的消費工作,這樣就可以在操作的過程中降低執行緒建立和銷燬的開銷,但是排程的開銷還是存在的。

有人給出了很好的歸納:

對作業系統來說,執行緒是最小的執行單元,程序是最小的資源管理單元。

在多核場景下,如果是I/O密集型場景,就算開多個執行緒來處理,也未必能提升CPU的利用率,反而會增加執行緒切換的開銷。另外,多執行緒之間假如存在臨界區或者共享資料,那麼同步的開銷也是不可忽視的。

協程,正好可以解決上面的相關問題。

這個時候我們的主角 協程 就要登場了。

協程,英文Coroutines,是一種比執行緒更加輕量級的存在。正如一個程序可以擁有多個執行緒一樣,一個執行緒也可以擁有多個協程。協程的排程完全由使用者控制。協程擁有自己的暫存器上下文和棧。協程排程切換時,將暫存器上下文和棧儲存到其他地方,在切回來的時候,恢復先前儲存的暫存器上下文和棧,直接操作棧則基本沒有核心切換的開銷,可以不加鎖的訪問全域性變數,所以上下文的切換非常快。協程與執行緒主要區別是它將不再被核心排程,而是交給了程式自己而執行緒是將自己交給核心排程,所以也不難理解golang中排程器的存在。

協程

定義:協程是輕量級執行緒

在一個使用者執行緒上可以跑多個協程,這樣就提高了單核的利用率。協程不像程序或者執行緒,可以讓系統負責相關的排程工作,協程是處於一個執行緒中,系統是無感知的,所以需要在該執行緒中阻塞某個協程的話,就需要手工進行排程。

  • 協程是一種使用者態的輕量級執行緒,協程的排程完全由使用者控制。
  • 一個執行緒可以擁有多個協程,協程不是被作業系統核心所管理,而完全是由程式所控制。
  • 與其讓作業系統排程,不如我自己來,這就是協程

 要在使用者執行緒上實現協程是一件很難受的事情,原理類似於排程器根據條件的改變不停地呼叫各個協程的callback機制,但是前提是大家都在一個使用者執行緒下。要注意,一旦有一個協程阻塞,其他協程也都不能運行了。因此要處理好協程。

最重要的是,協程不是被作業系統核心所管理,而完全是由程式所控制(也就是在使用者態執行)。

這樣帶來的好處就是效能得到了很大的提升,不會像執行緒切換那樣消耗資源。

這段程式碼十分簡單,即使沒用過python的小夥伴應該也能基本看懂。

程式碼中建立了一個叫做consumer的協程,並且在主執行緒中生產資料,協程中消費資料。

其中yield 是python當中的語法。當協程執行到yield關鍵字時,會暫停在那一行,等到主執行緒呼叫send方法傳送了資料,協程才會接到資料繼續執行。

但是,yield讓協程暫停,和執行緒的阻塞是有本質區別的。協程的暫停完全由程式控制,執行緒的阻塞狀態是由作業系統核心來進行切換。

因此,協程的開銷遠遠小於執行緒的開銷。

協程的應用

有哪些程式語言應用到了協程呢?我們舉幾個栗子:

Lua語言

Lua從5.0版本開始使用協程,通過擴充套件庫coroutine來實現。

Python語言

正如剛才所寫的程式碼示例,python可以通過 yield/send 的方式實現協程。在python 3.5以後, async/await 成為了更好的替代方案。

Go語言

Go語言對協程的實現非常強大而簡潔,可以輕鬆建立成百上千個協程併發執行。

Java語言

如上文所說,Java語言並沒有對協程的原生支援,但是某些開源框架模擬出了協程的功能,有興趣的小夥伴可以看一看Kilim框架的原始碼:

總結:

多程序的出現是為了提升CPU的利用率,特別是I/O密集型運算,不管是多核還是單核,開多個程序必然能有效提升CPU的利用率。而多執行緒則可以共享同一程序地址空間上的資源,為了降低執行緒建立和銷燬的開銷,又出現了執行緒池的概念,最後,為了提升使用者執行緒的最大利用效率,又提出了協程的概念。

通訊方式:

執行緒的生命週期
建立:執行緒從建立到被cpu執行之前的這個階段。
就緒:指執行緒已具備各種執行條件,一旦獲取cpu便可執行。
執行:表示執行緒正獲得cpu在執行。
阻塞:指執行緒在執行中因某件事而受阻,處於暫停執行的狀態,阻塞的執行緒不會去競爭cpu。
終止:執行緒執行完畢,接下來會釋放執行緒佔用的資源。

程序排程演算法