並行是讓不同的**片段同時在不同的物理處理器上執行。並行的關鍵是同時做很多事情,而併發是指同時管理很多事情,這些事情可能只做了一半就被暫停去做別的事情了。在很多情況下,併發的效果比並行好,因為作業系統和硬體的總資源一般很少,但能支援系統同時做很多事情。這種「使用較少的資源做更多的事情」的哲學,也是指導 go 語言設計的哲學。go語言使用goroutine來實現併發。
goroutine是go語言自己實現的乙個虛擬執行時。作業系統會在物理處理器上排程執行緒來執行,而 go 語言的執行時會在邏輯處理器上排程goroutine來執行。每個邏輯處理器都分別繫結到單個作業系統執行緒。在 1.5 及以後的版本,go語言的執行時缺省會為每個可用的物理處理器分配乙個邏輯處理器。在 1.5 版本之前的版本中,預設給整個應用程式只分配乙個邏輯處理器。這些邏輯處理器會用於執行所有被建立的goroutine。
在下圖中,可以看到作業系統執行緒、邏輯處理器和本地執行佇列之間的關係。如果建立乙個 goroutine 並準備執行,這個 goroutine 就會被放到排程器的全域性執行佇列中。之後,排程器就將這些佇列中的 goroutine 分配給乙個邏輯處理器,並放到這個邏輯處理器對應的本地執行佇列中。本地執行佇列中的 goroutine 會一直等待直到自己被分配的邏輯處理器執行。
有時,正在執行的 goroutine 需要執行乙個阻塞的系統呼叫,如開啟乙個檔案。當這類呼叫發生時,執行緒和 goroutine 會從邏輯處理器上分離,該執行緒會繼續阻塞,等待系統呼叫的返回。與此同時,這個邏輯處理器就失去了用來執行的執行緒。所以,排程器會建立乙個新執行緒,並將其繫結到該邏輯處理器上。之後,排程器會從本地執行佇列裡選擇另乙個 goroutine 來執行。一旦
被阻塞的系統呼叫執行完成並返回,對應的 goroutine 會放回到本地執行佇列,而之前的執行緒會儲存好,以便之後可以繼續使用。
如果乙個 goroutine 需要做乙個網路 i/o 呼叫,流程上會有些不一樣。在這種情況下,goroutine會和邏輯處理器分離,並移到整合了網路輪詢器的執行時。一旦該輪詢器指示某個網路讀或者寫操作已經就緒,對應的 goroutine 就會重新分配到邏輯處理器上來完成操作。排程器對可以建立的邏輯處理器的數量沒有限制,但語言執行時預設限制每個程式最多建立 10 000 個執行緒。這個限制值可以通過呼叫 runtime/debug 包的 setmaxthreads 方法來更改。如果程式試圖使用更多的執行緒,就會崩潰。
如果希望讓 goroutine 並行,必須使用多於乙個邏輯處理器。當有多個邏輯處理器時,排程器會將 goroutine 平等分配到每個邏輯處理器上。這會讓 goroutine 在不同的執行緒上執行。不過要想真的實現並行的效果,使用者需要讓自己的程式執行在有多個物理處理器的機器上。否則,哪怕 go 語言執行時使用多個執行緒,goroutine 依然會在同乙個物理處理器上併發執行,達不到並行的效果。
go語言中併發與並行的處理如下圖:
GO語言學習 併發
goroutine 由go執行環境管理的輕量級執行緒 channel 有型別的管道,操作符為 資料流向箭頭指向的方向 使用make chan 緩衝資料型別 緩衝區長度 來建立 使用close chan 來關閉管道,只有傳送者才允許關閉管道 例ch make chan int 10 ch 10 a c...
GO語言學習 併發
通道channel 併發 concurrency 邏輯上具備同時處理多個任務的能力。並行 parallesim 不同於併發,物理上的同一時刻,相當於併發設計的理想執行模式。在函式呼叫前新增go關鍵字即可建立併發任務 a 100go func x,y int a,counter a a 66print...
go語言學習備忘 方法
如果乙個函式有接收者,那麼這個函式就叫方法。go 語言裡有兩種型別的接收者 值接收者和指標接收者。如下 user 在程式裡定義乙個使用者型別 type user struct notify 使用值接收者實現了乙個方法 func u user notify changeemail 使用指標接收者實現了...