通過runtime.GOMAXPROCS函數(shù),應(yīng)用程序何以在運(yùn)行期間設(shè)置運(yùn)行時系統(tǒng)中得P最大數(shù)量。但這會引起“Stop the Word”。所以,應(yīng)在應(yīng)用程序最早的調(diào)用。并且最好的設(shè)置P最大值的方法是在運(yùn)行Go程序之前設(shè)置好操作程序的環(huán)境變量GOMAXPROCS,而不是在程序中調(diào)用runtime.GOMAXPROCS函數(shù)。
創(chuàng)新互聯(lián)-專業(yè)網(wǎng)站定制、快速模板網(wǎng)站建設(shè)、高性價比榮成網(wǎng)站開發(fā)、企業(yè)建站全套包干低至880元,成熟完善的模板庫,直接使用。一站式榮成網(wǎng)站制作公司更省心,省錢,快速模板網(wǎng)站建設(shè)找我們,業(yè)務(wù)覆蓋榮成地區(qū)。費(fèi)用合理售后完善,十余年實(shí)體公司更值得信賴。
最后記住,無論我們傳遞給函數(shù)的整數(shù)值是什么值,運(yùn)行時系統(tǒng)的P最大值總會在1~256之間。
runtime.Goexit函數(shù)被調(diào)用后,會立即使調(diào)用他的Groution的運(yùn)行被終止,但其他Goroutine并不會受到影響。runtime.Goexit函數(shù)在終止調(diào)用它的Goroutine的運(yùn)行之前會先執(zhí)行該Groution中還沒有執(zhí)行的defer語句。
runtime.Gosched函數(shù)的作用是暫停調(diào)用他的Goroutine的運(yùn)行,調(diào)用他的Goroutine會被重新置于Gorunnable狀態(tài),并被放入調(diào)度器可運(yùn)行G隊(duì)列中。
runtime.NumGoroutine函數(shù)在被調(diào)用后,會返回系統(tǒng)中的處于特定狀態(tài)的Goroutine的數(shù)量。這里的特指是指Grunnable\Gruning\Gsyscall\Gwaition。處于這些狀態(tài)的Groutine即被看做是活躍的或者說正在被調(diào)度。
注意:垃圾回收所在Groutine的狀態(tài)也處于這個范圍內(nèi)的話,也會被納入該計數(shù)器。
前者調(diào)用會使調(diào)用他的Goroutine與當(dāng)前運(yùn)行它的M鎖定到一起,后者調(diào)用會解除這樣的鎖定。
注意:
debug.SetMaxStack函數(shù)的功能是約束單個Groutine所能申請的??臻g的最大尺寸。
debug.SetMaxThreads函數(shù)的功能是對go語言運(yùn)行時系統(tǒng)所使用的內(nèi)核線程的數(shù)量(確切的說是M的數(shù)量)進(jìn)行設(shè)置
會讓運(yùn)行時系統(tǒng)進(jìn)行一次強(qiáng)制性的垃圾收集,
用于設(shè)置一個比率(垃圾收集比率),前面所說的單元增量與前一次垃圾收集時的歲內(nèi)存的單元數(shù)量和此垃圾手機(jī)比率有關(guān)。
觸發(fā)垃圾收集的堆內(nèi)存單元增量=上一次垃圾收集完的堆內(nèi)存單元數(shù)量*(垃圾收集比率/100)
本文是對 Gopher 2017 中一個非常好的 Talk?: [Understanding Channel](GopherCon 2017: Kavya Joshi - Understanding Channels) 的學(xué)習(xí)筆記,希望能夠通過對 channel 的關(guān)鍵特性的理解,進(jìn)一步掌握其用法細(xì)節(jié)以及 Golang 語言設(shè)計哲學(xué)的管窺蠡測。
channel 是可以讓一個 goroutine 發(fā)送特定值到另一個 gouroutine 的通信機(jī)制。
原生的 channel 是沒有緩存的(unbuffered channel),可以用于 goroutine 之間實(shí)現(xiàn)同步。
關(guān)閉后不能再寫入,可以讀取直到 channel 中再沒有數(shù)據(jù),并返回元素類型的零值。
gopl/ch3/netcat3
首先從 channel 是怎么被創(chuàng)建的開始:
在 heap 上分配一個 hchan 類型的對象,并將其初始化,然后返回一個指向這個 hchan 對象的指針。
理解了 channel 的數(shù)據(jù)結(jié)構(gòu)實(shí)現(xiàn),現(xiàn)在轉(zhuǎn)到 channel 的兩個最基本方法: sends 和 receivces ,看一下以上的特性是如何體現(xiàn)在 sends 和 receives 中的:
假設(shè)發(fā)送方先啟動,執(zhí)行 ch - task0 :
如此為 channel 帶來了 goroutine-safe 的特性。
在這樣的模型里, sender goroutine - channel - receiver goroutine 之間, hchan 是唯一的共享內(nèi)存,而這個唯一的共享內(nèi)存又通過 mutex 來確保 goroutine-safe ,所有在隊(duì)列中的內(nèi)容都只是副本。
這便是著名的 golang 并發(fā)原則的體現(xiàn):
發(fā)送方 goroutine 會阻塞,暫停,并在收到 receive 后才恢復(fù)。
goroutine 是一種 用戶態(tài)線程 , 由 Go runtime 創(chuàng)建并管理,而不是操作系統(tǒng),比起操作系統(tǒng)線程來說,goroutine更加輕量。
Go runtime scheduler 負(fù)責(zé)將 goroutine 調(diào)度到操作系統(tǒng)線程上。
runtime scheduler 怎么將 goroutine 調(diào)度到操作系統(tǒng)線程上?
當(dāng)阻塞發(fā)生時,一次 goroutine 上下文切換的全過程:
然而,被阻塞的 goroutine 怎么恢復(fù)過來?
阻塞發(fā)生時,調(diào)用 runtime sheduler 執(zhí)行 gopark 之前,G1 會創(chuàng)建一個 sudog ,并將它存放在 hchan 的 sendq 中。 sudog 中便記錄了即將被阻塞的 goroutine G1 ,以及它要發(fā)送的數(shù)據(jù)元素 task4 等等。
接收方 將通過這個 sudog 來恢復(fù) G1
接收方 G2 接收數(shù)據(jù), 并發(fā)出一個 receivce ,將 G1 置為 runnable :
同樣的, 接收方 G2 會被阻塞,G2 會創(chuàng)建 sudoq ,存放在 recvq ,基本過程和發(fā)送方阻塞一樣。
不同的是,發(fā)送方 G1如何恢復(fù)接收方 G2,這是一個非常神奇的實(shí)現(xiàn)。
理論上可以將 task 入隊(duì),然后恢復(fù) G2, 但恢復(fù) G2后,G2會做什么呢?
G2會將隊(duì)列中的 task 復(fù)制出來,放到自己的 memory 中,基于這個思路,G1在這個時候,直接將 task 寫到 G2的 stack memory 中!
這是違反常規(guī)的操作,理論上 goroutine 之間的 stack 是相互獨(dú)立的,只有在運(yùn)行時可以執(zhí)行這樣的操作。
這么做純粹是出于性能優(yōu)化的考慮,原來的步驟是:
優(yōu)化后,相當(dāng)于減少了 G2 獲取鎖并且執(zhí)行 memcopy 的性能消耗。
channel 設(shè)計背后的思想可以理解為 simplicity 和 performance 之間權(quán)衡抉擇,具體如下:
queue with a lock prefered to lock-free implementation:
比起完全 lock-free 的實(shí)現(xiàn),使用鎖的隊(duì)列實(shí)現(xiàn)更簡單,容易實(shí)現(xiàn)
可以參考下這個。在stack scan階段有一小段stw,和mark termination階段要stw。之前要stw的mark和sweep階段都是并行的,不需要stw的了。
里面也提到了1.5使用了write barrier的算法會導(dǎo)致吞吐量下降,1.6會根據(jù)實(shí)際使用情況平衡下延遲和吞吐量。
沒有stw也是可以的,但吞吐量會進(jìn)一步下降,未必是最佳選擇。