Go的CSP并發(fā)模型
創(chuàng)新互聯網站建設提供從項目策劃、軟件開發(fā),軟件安全維護、網站優(yōu)化(SEO)、網站分析、效果評估等整套的建站服務,主營業(yè)務為做網站、網站建設,app軟件開發(fā)以傳統(tǒng)方式定制建設網站,并提供域名空間備案等一條龍服務,秉承以專業(yè)、用心的態(tài)度為用戶提供真誠的服務。創(chuàng)新互聯深信只要達到每一位用戶的要求,就會得到認可,從而選擇與我們長期合作。這樣,我們也可以走得更遠!
Go實現了兩種并發(fā)形式。第一種是大家普遍認知的:多線程共享內存。其實就是Java或者C++等語言中的多線程開發(fā)。另外一種是Go語言特有的,也是Go語言推薦的:CSP(communicating sequential processes)并發(fā)模型。
CSP 是 Communicating Sequential Process 的簡稱,中文可以叫做通信順序進程,是一種并發(fā)編程模型,由 Tony Hoare 于 1977 年提出。簡單來說,CSP 模型由并發(fā)執(zhí)行的實體(線程或者進程)所組成,實體之間通過發(fā)送消息進行通信,這里發(fā)送消息時使用的就是通道,或者叫 channel。CSP 模型的關鍵是關注 channel,而不關注發(fā)送消息的實體。 Go 語言實現了 CSP 部分理論 。
“ 不要以共享內存的方式來通信,相反, 要通過通信來共享內存。”
Go的CSP并發(fā)模型,是通過 goroutine和channel 來實現的。
goroutine 是Go語言中并發(fā)的執(zhí)行單位。其實就是協(xié)程。
channel是Go語言中各個并發(fā)結構體(goroutine)之前的通信機制。 通俗的講,就是各個goroutine之間通信的”管道“,有點類似于Linux中的管道。
Channel
Goroutine
假設,現在有1w個任務,需要通過線程并發(fā)執(zhí)行。如果直接執(zhí)行,會把內存撐爆。需要讓這些任務一批一批執(zhí)行。如果設定,每次最多同時執(zhí)行10個任務。
需要用到通道channel,利用channel的limit,通過堵塞通道的方式,把進程堵住。
golang中最大協(xié)程數的限制
golang中有最大協(xié)程數的限制嗎?如果有的話,是通過什么參數控制呢?還是通過每個協(xié)程占用的資源計算?
通過channel控制協(xié)程數的就忽略吧。
以我的理解,計算機資源肯定是有限的,所以goroutine肯定也是有限制的,單純的goroutine,一開始每個占用4K內存,所以這里會受到內存使用量的限制,還有goroutine是通過系統(tǒng)線程來執(zhí)行的,golang默認最大的線程數是10000個??梢酝ㄟ^
來修改。但要注意線程和goroutine不是一一對應關系,理論上內存足夠大,而且goroutine不是計算密集型的話,可以開啟無限個goroutine。
參考:
Goroutine并發(fā)調度模型深度解析手擼一個協(xié)程池
Golang 的 goroutine 是如何實現的?
Golang - 調度剖析【第二部分】
OS線程初始棧為2MB。Go語言中,每個goroutine采用動態(tài)擴容方式,初始2KB,按需增長,最大1G。此外GC會收縮??臻g。
BTW,增長擴容都是有代價的,需要copy數據到新的stack,所以初始2KB可能有些性能問題。
更多關于stack的內容,可以參見大佬的文章。 聊一聊goroutine stack
用戶線程的調度以及生命周期管理都是用戶層面,Go語言自己實現的,不借助OS系統(tǒng)調用,減少系統(tǒng)資源消耗。
Go語言采用兩級線程模型,即用戶線程與內核線程KSE(kernel scheduling entity)是M:N的。最終goroutine還是會交給OS線程執(zhí)行,但是需要一個中介,提供上下文。這就是G-M-P模型
Go調度器有兩個不同的運行隊列:
go1.10\src\runtime\runtime2.go
Go調度器根據事件進行上下文切換。
調度的目的就是防止M堵塞,空閑,系統(tǒng)進程切換。
詳見 Golang - 調度剖析【第二部分】
Linux可以通過epoll實現網絡調用,統(tǒng)稱網絡輪詢器N(Net Poller)。
文件IO操作
上面都是防止M堵塞,任務竊取是防止M空閑
每個M都有一個特殊的G,g0。用于執(zhí)行調度,gc,棧管理等任務,所以g0的棧稱為調度棧。g0的棧不會自動增長,不會被gc,來自os線程的棧。
go1.10\src\runtime\proc.go
G沒辦法自己運行,必須通過M運行
M通過通過調度,執(zhí)行G
從M掛載P的runq中找到G,執(zhí)行G