進程、線程、輕量級進程、協程與 go 的 goroutine

本文內容

  • 進程
  • 線程
  • 協程
  • Go 中的 goroutine
  • 參考資料

最近,看一些文章,提到「協程」的概念,心想,進程,線程,協程,前兩個很容易,任何一本關於操做系統的書都有說,開發時也常常用,可是協程呢?以前也遇到這個詞,可是今天,查了一下資料。下面是一我的的總結。php

 

遷移到:http://www.bdata-cap.com/newsinfo/1713872.html

電話面試被問到go的協程……雖然用 python 時候在 Eurasia 和 eventlet 裏瞭解過協程,但本身對協程的概念也就是輕量級線程,還有一個很通俗的紅綠燈說法:線程要守規則,協程看到紅燈可是沒有車仍能夠通行。如今總結各個資料,從我的理解上說明下進程、線程、輕量級進程、協程,以及 go 中的 goroutine 那些事兒。html

進程


操做系統中最核心的概念是進程,分佈式系統中最重要的問題是進程間通訊。java

進程是「程序執行的一個實例」,擔當分配系統資源的實體。進程建立必須分配一個完整的獨立地址空間。python

進程切換隻發生在內核態,兩步:golang

  • 切換頁全局目錄以安裝一個新的地址空間;
  • 切換內核態堆棧和硬件上下文。 

另外一種說法相似:面試

  • 保存CPU環境(寄存器值、程序計數器、堆棧指針);
  • 修改內存管理單元MMU的寄存器;
  • 轉換後備緩衝器TLB中的地址轉換緩存內容標記爲無效。

線程


線程是進程的一個執行流,獨立執行它本身的程序代碼。線程(thread)是操做系統可以進行運算調度的最小單位。算法

線程上下文通常只包含CPU上下文及其餘的線程管理信息。線程建立的開銷主要取決於爲線程堆棧的創建而分配內存的開銷,這些開銷並不大。線程上下文切換髮生在兩個線程須要同步的時候,好比進入共享數據段。切換隻CPU寄存器值須要存儲,並隨後用將要切換到的線程的原先存儲的值從新加載到CPU寄存器中去。編程

用戶級線程主要缺點在於對引發阻塞的系統調用的調用會當即阻塞該線程所屬的整個進程。內核實現線程則會致使線程上下文切換的開銷跟進程同樣大,因此折衷的方法是輕量級進程(Lightweight)。在 Linux 中,一個線程組基本上就是實現了多線程應用的一組輕量級進程。進程中存在用戶線程、輕量級進程、內核線程。緩存

語言層面實現輕量級進程的比較少,stackless python,erlang 支持,java 並不支持。網絡

協程


協程是輕量級的線程,一個進程可輕鬆建立數十萬計的協程。從維基百科上看,從Knuth老爺子的基本算法捲上看「子程序實際上是協程的特例」。子程序是什麼?子程序(Subroutine, procedure, function, routine, method, subprogram),就是函數嘛!因此協程也沒什麼了不得的,就是更通常意義的程序組件,那你內存空間夠大,建立多少個函數還不是隨你嗎?

協程能夠經過yield來調用其它協程。經過yield方式轉移執行權的協程之間不是調用者與被調用者的關係,而是彼此對稱、平等的。協程的起始處是第一個入口點,在協程裏,返回點以後是接下來的入口點。子例程的生命期遵循後進先出(最後一個被調用的子例程最早返回);相反,協程的生命期徹底由他們的使用的須要決定。

線程和協程的區別?

一旦建立完線程,你就沒法決定他何時得到時間片,何時讓出時間片了,你把它交給了內核。而協程編寫者,一是可控的切換時機,二是很小的切換代價。從操做系統有沒有調度權上看,協程就是由於不須要進行內核態的切換,因此會使用它,會有這麼個東西。賴永浩和dccmx 這個定義我以爲相對準確  協程-用戶態的輕量級的線程。(http://blog.dccmx.com/2011/04/coroutine-concept/)

爲何要用協程?

協程有助於實現:

  • 狀態機:在一個子例程裏實現狀態機,這裏狀態由該過程當前的出口/入口點肯定;這能夠產生可讀性更高的代碼。
  • 角色模型:並行的角色模型,例如計算機遊戲。每一個角色有本身的過程(這又在邏輯上分離了代碼),但他們自願地向順序執行各角色過程的中央調度器交出控制(這是合做式多任務的一種形式)。
  • 產生器:它有助於輸入/輸出和對數據結構的通用遍歷。

語言/平臺

實現版本

協程名稱

備註

GoLang

原生支持

goroutine

 

Erlang

原生支持

process

函數式編程

Scala

原生支持

actor

函數式編程

Python

2.5 版本後

coroutine

官方 Python 不徹底實現,Stackless Python 支持

Perl

6.0 版本後

coroutine

 

Ruby

1.9 版本後

fiber

 

Lua

原生支持

coroutine

 

C#

.Net  2.0 版本後

fiber

 

Go 中的 goroutine


go中的Goroutine, 廣泛認爲是協程的go語言實現。《Go語言編程》中說goroutine是輕量級線程(即協程coroutine, 原書90頁). 在第九章進階話題中, 做者又一次提到, "從根本上來講, goroutine就是一種go語言版本的協程(coroutine)" (原書204頁). 但做者Rob Pike並不這麼說。

「一個Goroutine是一個與其餘goroutines 併發運行在同一地址空間的Go函數或方法。一個運行的程序由一個或更多個goroutine組成。它與線程、協程、進程等不一樣。它是一個goroutine。」

在棧實現上,它的編譯器分支下的實現gccgo是線程pthread,6g上是多路複用的threads(6g/8g/5g分別表明64位、32位及Arm架構編譯器)

infoQ一篇文章介紹特性也說道: goroutine是Go語言運行庫的功能,不是操做系統提供的功能,goroutine不是用線程實現的。具體可參見Go語言源碼裏的pkg/runtime/proc.c

老趙認爲goroutine就是把類庫功能放進了語言裏。

goroutine的併發問題:goroutine在共享內存中運行,通訊網絡可能死鎖,多線程問題的調試糟糕透頂等等。一個比較好的建議規則:不要經過共享內存通訊,相反,經過通訊共享內存。

並行與併發區別:

並行是指程序的運行狀態,要有兩個線程正在執行才能算是Parallelism;併發指程序的邏輯結構,Concurrency則只要有兩個以上線程還在執行過程當中便可。簡單地說,Parallelism要在多核或者多處理器狀況下才能作到,而 Concurrency則不須要。(http://stackoverflow.com/questions/1050222/concurrency- vs-parallelism-what-is-the-difference)

參考資料


  • https://zh.wikipedia.org/wiki/Go
  • https://zh.wikipedia.org/wiki/進程
  • https://zh.wikipedia.org/wiki/線程
相關文章
相關標籤/搜索