首頁 > 後端開發 > Golang > golang並發不是並行

golang並發不是並行

(*-*)浩
發布: 2019-12-17 11:52:22
原創
2213 人瀏覽過

golang並發不是並行

並發不等於平行

golang的核心開發人員Rob Pike專門提到了這個主題                   #            建議學習中:   # go

雖然我們在for迴圈中使用了go 創建了一個goroutine,我們想當然會認為,每次循環變數時,golang一定會執行這個goroutine,然後輸出當時的變數。 

這時,我們就陷入了思維定勢。預設並發等於並行。

誠然,透過go創建的goroutine是會並發的執行其中的函數程式碼。

 但一定會按照我們所設想的那樣每次循環時執行嗎?

 答案是否定的!

Rob Pike特別提到了golang中並髮指的是程式碼結構中的某些函數邏輯上可以同時執行,但物理上未必會同時執行。而並行則指的就是在物理層面也就是使用了不同CPU在執行不同或相同的任務。

golang的goroutine調度模型決定了,每個goroutine是運行在虛擬CPU中的(也就是我們透過runtime.GOMAXPROCS(1)所設定的虛擬CPU個數)。

 虛擬CPU個數未必會和實際CPU個數相吻合。每個goroutine都會被一個特定的P(虛擬CPU)選定維護,而M(物理計算資源)每次回挑選一個有效P,然後執行P中的goroutine。

每個P會將自己所維護的goroutine放到一個G隊列中,其中就包括了goroutine堆疊訊息,是否可執行資訊等等。預設情況下,P的數量與實際物理CPU的數量相等。

因此當我們透過循環來創建goroutine時,每個goroutine會被分配到不同的P隊列中。而M的數量又不是唯一的,當M隨機挑選P時,也就等同隨機挑選了goroutine。

在本題中,我們設定了P=1。所以所有的goroutine會被綁定到同一個P。如果我們修改runtime.GOMAXPROCS的值,就會看到另外的順序。如果我們輸出goroutine id,就可以看到隨機挑選的效果:

func main() {
wg := sync.WaitGroup{}
wg.Add(20)
for i := 0; i < 10; i++ {
go func() {
var buf [64]byte
n := runtime.Stack(buf[:], false)
idField := strings.Fields(strings.TrimPrefix(string(buf[:n]), "goroutine "))[0]
id, err := strconv.Atoi(idField)
if err != nil {
panic(fmt.Sprintf("cannot get goroutine id: %v", err))
}
fmt.Println("go routine 1 i: ", i, id)
wg.Done()
}()
}
for i := 0; i < 10; i++ {
go func(i int) {
var buf [64]byte
n := runtime.Stack(buf[:], false)
idField := strings.Fields(strings.TrimPrefix(string(buf[:n]), "goroutine "))[0]
id, err := strconv.Atoi(idField)
if err != nil {
panic(fmt.Sprintf("cannot get goroutine id: %v", err))
}
fmt.Println("go routine 2 i: ", i, id)
wg.Done()
}(i)

}
wg.Wait()
}
登入後複製

輸出如下:

go routine 2 i: 9 24
go routine 1 i: 10 11
go routine 1 i: 10 5
go routine 1 i: 10 6
go routine 2 i: 3 18
go routine 1 i: 10 9
go routine 1 i: 10 10
go routine 1 i: 10 8
go routine 2 i: 0 15
go routine 2 i: 4 19
go routine 2 i: 6 21
go routine 1 i: 10 7
go routine 1 i: 10 14
go routine 2 i: 7 22
go routine 2 i: 8 23
go routine 1 i: 10 13
go routine 2 i: 5 20
go routine 1 i: 10 12
go routine 2 i: 1 16
go routine 2 i: 2 17
⋊> ~/S/g/g/s/t/C/goroutine ./goroutine
go routine 1 i: 10 11
go routine 2 i: 9 24
go routine 1 i: 10 6
go routine 1 i: 10 14
go routine 1 i: 10 9
go routine 1 i: 10 10
go routine 1 i: 10 12
go routine 2 i: 0 15
go routine 1 i: 10 13
go routine 1 i: 10 5
go routine 2 i: 1 16
go routine 2 i: 5 20
go routine 1 i: 10 7
go routine 2 i: 7 22
go routine 2 i: 3 18
go routine 2 i: 2 17
go routine 2 i: 4 19
go routine 1 i: 10 8
go routine 2 i: 8 23
go routine 2 i: 6 21
登入後複製

我們再回到這題中,雖然在循環中透過go定義了一個goroutine。但我們說到了,並發不等於並行。因此雖然定義了,但此刻不見得就會去執行。

要等待M選擇P之後,才能去執行goroutine。關於golang中goroutine是如何進行調度的(GPM模型),可以參考Scalable Go Scheduler Design Doc或LearnConcurrency

###

以上是golang並發不是並行的詳細內容。更多資訊請關注PHP中文網其他相關文章!

相關標籤:
來源:php.cn
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
作者最新文章
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板