006.併發
1 概念
1.1 goroutine是go並行設計的核心,goroutine的本質是輕量級執行緒
1.2 golang的runtime實現了對輕量級執行緒即goroutine的智慧型排程管理
1.3 p、m、g原理
1.3.1 runtime有p、m、g三個概念,p對應作業系統程序--對程式的抽象, w對應作業系統執行緒--對暫存器的抽象,g對應goroutine--go實現的輕量級執行緒, 也即greenthreads使用者態執行緒 p、m由核心負責排程,g由runtime負責排程,也能實現被多個處理器呼叫 p用m執行g,並且runtime內部維護了乙個g的佇列deque存放當前可執行的g, 當g執行結束,m空閒下來,p從deque中取出下乙個g繼續在m中執行
1.3.2 當g執行了帶阻塞的系統呼叫時,m會被阻塞進而被系統掛起, p會將g留在m繼續執行,並從deque取出新的g、新啟動乙個m來執行g 當g-m重新變成就緒狀態之後,p又會在合適的狀態繼續執行g-m
1.3.3 當p物件的g全部執行完之後,可以從別的p物件的deque中拿一半的g放到自己的deque執行
1.3.4 進一步優化
1.3.4.1 如果g阻塞,p會建立新的m裝載g執行
1.3.4.2 這在分布式、高併發的網路rpc/web場景下非常浪費系統資源導致效率低下
1.3.4.3 go的優勢就在於,在可能導致阻塞的系統呼叫上, 儘管go提供的系統呼叫介面為阻塞方式呼叫,但是內部實現是非阻塞, 當goroutine進行系統呼叫阻塞之後,當前的g被設定為阻塞,但是m並不會被阻塞, 仍然可以繼續取其他的g繼續執行,這樣就不會建立新的m,增加系統開銷
1.3.4.4 goroutine被設定為阻塞之後什麼時候再次被設定為就緒狀態,go抽象了netpoller物件對io進行多路復用, 在linux上使用epoll實現,當g阻塞時,netpoller將阻塞的檔案描述符註冊到epoll示例進行epoll_wait, 檔案描述符就緒之後通知給阻塞的g,g就緒之後就可以繼續執行了 這種方式避免了傳統的多程序、多執行緒堆積方式, golang使每乙個執行緒盡可能的忙碌起來進行計算,而不是阻塞在io呼叫上, 這在高併發的網路通訊場景下非常有效
1.3.5 參考資料
1.3.5.1
1.3.5.2
1.3.5.3
1.3.5.4
1.3.5.5 記憶體上限問題
2 goroutine使用
2.1 goroutine通過go關鍵字實現,其實就是乙個普通的函式,如:go hello(a, b, c)
2.2 與執行緒思路一樣,main相當於主線程,即主goroutine,如果不設定,主goroutine不會主動等待所有goroutine結束之後結束main
2.3 gomaxprocs設定問題
2.3.1 預設情況下,排程器僅使用單執行緒,也就是說只實現了併發
2.3.2 想要發揮多核處理器的並行,需要在我們的程式中顯式呼叫 runtime.gomaxprocs(n)
2.3.3 參考
2.3.3.1
2.3.3.2
2.3.3.3
2.3.3.4
2.3.3.5
2.4 runtime.gosched()表示讓cpu把時間片讓給別人,下次某個時候繼續恢復執行該goroutine
3 channel使用
3.1 go提供了乙個很好的機制channel供goroutine之間相互通訊
3.2 channel可以與unix shell 中的雙向管道做模擬:可以通過它傳送或者接收值,也可以直接認為就是執行緒佇列
3.3 需要注意: 定義乙個channel時,也需要定義傳送到channel的值的型別 而且必須使用make建立初始化channel
3.4 channel通過操作符
3.5 unbuffered channel
3.5.1 預設情況下,channel是不帶快取的 也就是說,channel接收和傳送資料都是阻塞的,除非另一端已經準備好 所以如果使用預設的channel,則必須有多個goroutine存在, 單個goroutine不可能既準備好了傳送資料也同時準備好了接收資料 所以說: 無緩衝channel是在多個goroutine之間同步很棒的工具,這也是無緩衝channel的主要用途
3.5.2 錯誤示例
3.5.2.1
3.5.3 錯誤結果
3.5.3.1
3.5.4 常見問題參考
3.5.4.1
3.5.4.2
3.5.4.3
3.5.4.4
3.5.4.5
3.5.4.6
3.5.5 sync.waitgroup實現routine同步
3.5.5.1 sync.waitgroup也可以用於控制main routine等待所有子routine完成
3.5.5.2
3.5.6 select監控多個channel,主動傳送quit訊號
3.5.6.2 可以監控多個channel
3.5.6.3 可以監控沒有可讀可寫的訊息
3.5.6.4 可以設定超時控制
3.6 buffered channel
3.6.1 長度為1的channel不是預設的非快取channel,非快取channel長度相當於0,所以是阻塞的!
3.6.2 語法: ch := make(chan type, value)
3.6.3 channel遍歷
3.6.3.1 for i := range c // until channel c closed
3.6.3.2 前提是c必須要在生產者中正確關閉,否則main routine出現異常,沒有資料繼續寫入或者讀出
3.6.3.3 for i := range c能夠不斷的讀取channel裡面的資料,直到該channel被顯式的關閉
3.6.3.4 測試是否關閉
3.6.3.4.1 v, ok :=
3.6.3.5 應該在生產者的地方關閉channel,而不是消費的地方去關閉它,這樣容易引起panic
3.6.3.6 channel不像檔案之類的,不需要經常去關閉,只有當你確實沒有任何傳送資料了, 或者你想顯式的結束range迴圈之類的
4 runtime
4.1 goexit
4.1.1 退出當前執行的goroutine,但是defer函式還會繼續呼叫
4.2 gosched
4.2.1 讓出當前goroutine的執行許可權,排程器安排其他等待的任務執行,並在下次某個時候從該位置恢復執行
4.3 numcpu
4.3.1 返回 cpu 核數量,此處是邏輯核,包括超執行緒
4.4 numgoroutine
4.4.1 返回正在執行和排隊的任務總數
4.5 gomaxprocs
4.5.1 用來設定可以平行計算的cpu核數的最大值,並返回之前的值
golang 閒談併發
對於併發這個概念,我想大家都對它不會陌生,今天就從簡單的火車站賣票問題出發,來談談併發。首先宣告本文的 是golang 因為最近開始用的就是golang 對於其他的語言其實也是相通的,那麼正式開始正題吧,首先我們來看看,賣一張票,總票數就減一,一般來說我們會這麼寫 package main impo...
golang 併發實踐
golang 高併發主要是依靠sync包下的api實現,首先就是waitgroup 先說說waitgroup的用途 它能夠一直等到所有的goroutine執行完成,並且阻塞主線程的執行,直到所有的goroutine執行完成。waitgroup總共有三個方法 add delta int done wa...
golang 併發 並行
go 語言的執行緒是併發機制,不是並行機制。那麼,什麼是併發,什麼是並行?併發是不同的 塊交替執行,也就是交替可以做不同的事情。並行是不同的 塊同時執行,也就是同時可以做不同的事情。舉個生活化場景的例子 你正在家看書,忽然 來了,然後你接 通話完成後繼續看書,這就是併發,看書和接 交替做。如果 來了...