簡單理解 Goroutine 是如何工做的

新公司使用 Golang,Golang 的魔力之一就是能夠開啓成千上萬的 goroutine 來處理併發,因而上網看一些簡單的關於 Goroutine 的介紹 https://blog.nindalf.com/post...
後期再深刻了解 Go Runtime 是如何管理和調度 goroutine編程

Go 語言介紹

若是你第一次接觸 GO 編程,或者你對「併發不是並行這句話」沒有任何概念,你能夠先去看一下 Rob Pike 的演講excellent talk on the subject,這30分鐘的演講你值得擁有服務器

當人們聽到 concurrency(併發) 這個詞每每會聯想到 parallelism(並行),他們是有關聯但徹底不同的概念。在編程的世界中,concurrency 是獨立執行的過程的組合,而 parallelism 則是計算任務的同時執行。concurrency 是在一段時間內處理多個任務,parallelism 是同時作多個任務。
Go 可讓咱們去編寫併發的程序,Go提供了 goroutine 和 goroutine 之間相互通訊的能力。本文會更多地關注 goroutine併發

Goroutine 和 OS Thread

Go 使用 goroutine 處理併發,而 Java 則使用 thread(線程)。爲了比較 goroutine 和 thread 的區別,咱們關注如下三個方面——內存使用,開啓和銷燬,切換時間工具

內存使用

建立 goroutine 不須要太多的內存,2KB的棧內存足矣,隨後會伴隨堆內存的分配和釋放而增加。線程的開啓則須要1MB內存 (goroutine的500倍),並伴隨着一片警惕內存頁(guard page)的分配,做爲線程棧內存以前的警惕區域。
因此,服務器在接收請求的時候能夠爲每一個請求分配一個 goroutine 來處理,並不會有內存問題。可是一請求一線程 (例如Java bio 編程) 模式頗有可能會致使 OutOfMemoryError,這不只僅出如今 Java (bio) 編程中,那些使用OS線程處理併發的語言大多都須要面臨這類問題。post

Goroutine 建立和銷燬

線程的建立和銷燬有很大的開銷,由於咱們必須向 OS 請求線程資源,而且在線程完成後歸還,維護線程池是一個很好的應對方法。相反,Go Runtime 花費很小的開銷就能建立和銷燬 goroutine,Go語言也沒有提供對 goroutine 的人工管理接口 (意思就是 Go 已經安排好了,你不用瞎操心去管理 goroutine)性能

Goroutine 切換開銷

當線程阻塞時,另外一個線程會被調度。線程的調度是搶佔式的,在切換過程當中,調度器必須保存和恢復全部寄存器狀態,包括:16個通用寄存器,程序計數器 PC,棧指針 SP,段寄存器,16個 XMM 寄存器,16個 AVX 寄存器,FP 協處理器狀態等等。系統頻繁地進行線程切換將會帶來巨大的開銷。
Goroutine 的調度是協做式的(因此被稱爲 go 協程?)。在進行 goroutine 切換時,只有3個寄存器須要被存儲和恢復,他們是程序計數器 PC,棧指針和通用寄存器 DX,開銷很小。
Goroutine 的數量一般是巨大的,但這不會影響 goroutine 的切換時間。調度器只會關注運行狀態的 goroutine,而忽略阻塞態的 goroutine。Go 跟現代調度器同樣都是 O(1) 單位時間複雜度,意味着增長 goroutine 數量不會增長切換時間線程

Goroutine 如何執行

以前說起過,Go Runtime 管理了 goroutine 的建立,調度和銷燬。Go Runtime 事先分配了一些線程,全部的 goroutine 都在這些線程上多路複用。在某個時刻,每一個線程只會裝載執行一個 goroutine,若是那個 goroutine 被阻塞了,他會被換出,並由另外一個 goroutine 得到線程執行
由於 goroutine 的調度是協做式的,一個執行無限循環任務的 goroutine 會「餓死」其餘在相同線程上的 goroutine(其餘 goroutine 沒法搶佔得到線程)。這個問題在 Go 1.2 中有所緩解,經過在 goroutine 進入一個方法時偶爾去調用調度器(執行調度),因此一個包含方法執行的無限循環是可被搶佔的。設計

Goroutine 阻塞

Goroutine 的阻塞不會致使線程的阻塞。即便成千上萬的 goroutine 被建立,即便他們大多數都被阻塞了,但只要 Go Runtime 調度其餘可用的 goroutine,就不會形成系統資源浪費
用簡單的話說,goroutine 是一種更輕量級的 OS 線程的抽象。Go 開發者不須要管理線程,一樣OS也感知不到 goroutine 的存在。在 OS 的視角下,Go 程序的就像一個事件驅動的 C 程序。指針

線程和CPU

雖然你不能直接控制 Go Runtime 建立線程的數量,你仍可控制程序使用的 CPU 核心數,經過runtime.GOMAXPROCS(n)來設置 GOMAXPROCS。增長 CPU 核心數也許並不能顯著提升程序的性能,但你可使用工具來找到程序運行最理想的 CPU 核心數excel

總結

像其餘語言同樣,你應該儘量避免讓多個 goroutine 同時訪問共享資源。goroutine 之間不要使用共享內存進行通訊,最好的作法是使用 channel 在他們之間傳輸數據。
最後我強烈建議你閱讀 C.A.R.Hoare 的文章 Communicating Sequential Processes。在文章中,他預言單核心 CPU 會最終到達性能瓶頸,芯片製造者將會堆積核心數量。他所表達的觀點對 GO 語言的設計有着深遠的影響。

相關文章
相關標籤/搜索