Goroutine調(diào)度是一個(gè)很復(fù)雜的機(jī)制,下面嘗試用簡(jiǎn)單的語(yǔ)言描述一下Goroutine調(diào)度機(jī)制,想要對(duì)其有更深入的了解可以去研讀一下源碼。

創(chuàng)新互聯(lián)公司是創(chuàng)新、創(chuàng)意、研發(fā)型一體的綜合型網(wǎng)站建設(shè)公司,自成立以來(lái)公司不斷探索創(chuàng)新,始終堅(jiān)持為客戶提供滿意周到的服務(wù),在本地打下了良好的口碑,在過(guò)去的十多年時(shí)間我們累計(jì)服務(wù)了上千家以及全國(guó)政企客戶,如成都OPP膠袋等企業(yè)單位,完善的項(xiàng)目管理流程,嚴(yán)格把控項(xiàng)目進(jìn)度與質(zhì)量監(jiān)控加上過(guò)硬的技術(shù)實(shí)力獲得客戶的一致贊譽(yù)。
首先介紹一下GMP什么意思:
G ----------- goroutine: 即Go協(xié)程,每個(gè)go關(guān)鍵字都會(huì)創(chuàng)建一個(gè)協(xié)程。
M ---------- thread內(nèi)核級(jí)線程,所有的G都要放在M上才能運(yùn)行。
P ----------- processor處理器,調(diào)度G到M上,其維護(hù)了一個(gè)隊(duì)列,存儲(chǔ)了所有需要它來(lái)調(diào)度的G。
Goroutine 調(diào)度器P和 OS 調(diào)度器是通過(guò) M 結(jié)合起來(lái)的,每個(gè) M 都代表了 1 個(gè)內(nèi)核線程,OS 調(diào)度器負(fù)責(zé)把內(nèi)核線程分配到 CPU 的核上執(zhí)行
模型圖:
避免頻繁的創(chuàng)建、銷毀線程,而是對(duì)線程的復(fù)用。
1)work stealing機(jī)制
當(dāng)本線程無(wú)可運(yùn)行的G時(shí),嘗試從其他線程綁定的P偷取G,而不是銷毀線程。
2)hand off機(jī)制
當(dāng)本線程M0因?yàn)镚0進(jìn)行系統(tǒng)調(diào)用阻塞時(shí),線程釋放綁定的P,把P轉(zhuǎn)移給其他空閑的線程執(zhí)行。進(jìn)而某個(gè)空閑的M1獲取P,繼續(xù)執(zhí)行P隊(duì)列中剩下的G。而M0由于陷入系統(tǒng)調(diào)用而進(jìn)被阻塞,M1接替M0的工作,只要P不空閑,就可以保證充分利用CPU。M1的來(lái)源有可能是M的緩存池,也可能是新建的。當(dāng)G0系統(tǒng)調(diào)用結(jié)束后,根據(jù)M0是否能獲取到P,將會(huì)將G0做不同的處理:
如果有空閑的P,則獲取一個(gè)P,繼續(xù)執(zhí)行G0。
如果沒有空閑的P,則將G0放入全局隊(duì)列,等待被其他的P調(diào)度。然后M0將進(jìn)入緩存池睡眠。
如下圖
GOMAXPROCS設(shè)置P的數(shù)量,最多有GOMAXPROCS個(gè)線程分布在多個(gè)CPU上同時(shí)運(yùn)行
在Go中一個(gè)goroutine最多占用CPU 10ms,防止其他goroutine被餓死。
具體可以去看另一篇文章
【Golang詳解】go語(yǔ)言調(diào)度機(jī)制 搶占式調(diào)度
當(dāng)創(chuàng)建一個(gè)新的G之后優(yōu)先加入本地隊(duì)列,如果本地隊(duì)列滿了,會(huì)將本地隊(duì)列的G移動(dòng)到全局隊(duì)列里面,當(dāng)M執(zhí)行work stealing從其他P偷不到G時(shí),它可以從全局G隊(duì)列獲取G。
協(xié)程經(jīng)歷過(guò)程
我們創(chuàng)建一個(gè)協(xié)程 go func()經(jīng)歷過(guò)程如下圖:
說(shuō)明:
這里有兩個(gè)存儲(chǔ)G的隊(duì)列,一個(gè)是局部調(diào)度器P的本地隊(duì)列、一個(gè)是全局G隊(duì)列。新創(chuàng)建的G會(huì)先保存在P的本地隊(duì)列中,如果P的本地隊(duì)列已經(jīng)滿了就會(huì)保存在全局的隊(duì)列中;處理器本地隊(duì)列是一個(gè)使用數(shù)組構(gòu)成的環(huán)形鏈表,它最多可以存儲(chǔ) 256 個(gè)待執(zhí)行任務(wù)。
G只能運(yùn)行在M中,一個(gè)M必須持有一個(gè)P,M與P是1:1的關(guān)系。M會(huì)從P的本地隊(duì)列彈出一個(gè)可執(zhí)行狀態(tài)的G來(lái)執(zhí)行,如果P的本地隊(duì)列為空,就會(huì)想其他的MP組合偷取一個(gè)可執(zhí)行的G來(lái)執(zhí)行;
一個(gè)M調(diào)度G執(zhí)行的過(guò)程是一個(gè)循環(huán)機(jī)制;會(huì)一直從本地隊(duì)列或全局隊(duì)列中獲取G
上面說(shuō)到P的個(gè)數(shù)默認(rèn)等于CPU核數(shù),每個(gè)M必須持有一個(gè)P才可以執(zhí)行G,一般情況下M的個(gè)數(shù)會(huì)略大于P的個(gè)數(shù),這多出來(lái)的M將會(huì)在G產(chǎn)生系統(tǒng)調(diào)用時(shí)發(fā)揮作用。類似線程池,Go也提供一個(gè)M的池子,需要時(shí)從池子中獲取,用完放回池子,不夠用時(shí)就再創(chuàng)建一個(gè)。
work-stealing調(diào)度算法:當(dāng)M執(zhí)行完了當(dāng)前P的本地隊(duì)列隊(duì)列里的所有G后,P也不會(huì)就這么在那躺尸啥都不干,它會(huì)先嘗試從全局隊(duì)列隊(duì)列尋找G來(lái)執(zhí)行,如果全局隊(duì)列為空,它會(huì)隨機(jī)挑選另外一個(gè)P,從它的隊(duì)列里中拿走一半的G到自己的隊(duì)列中執(zhí)行。
如果一切正常,調(diào)度器會(huì)以上述的那種方式順暢地運(yùn)行,但這個(gè)世界沒這么美好,總有意外發(fā)生,以下分析goroutine在兩種例外情況下的行為。
Go runtime會(huì)在下面的goroutine被阻塞的情況下運(yùn)行另外一個(gè)goroutine:
用戶態(tài)阻塞/喚醒
當(dāng)goroutine因?yàn)閏hannel操作或者network I/O而阻塞時(shí)(實(shí)際上golang已經(jīng)用netpoller實(shí)現(xiàn)了goroutine網(wǎng)絡(luò)I/O阻塞不會(huì)導(dǎo)致M被阻塞,僅阻塞G,這里僅僅是舉個(gè)栗子),對(duì)應(yīng)的G會(huì)被放置到某個(gè)wait隊(duì)列(如channel的waitq),該G的狀態(tài)由_Gruning變?yōu)開Gwaitting,而M會(huì)跳過(guò)該G嘗試獲取并執(zhí)行下一個(gè)G,如果此時(shí)沒有可運(yùn)行的G供M運(yùn)行,那么M將解綁P,并進(jìn)入sleep狀態(tài);當(dāng)阻塞的G被另一端的G2喚醒時(shí)(比如channel的可讀/寫通知),G被標(biāo)記為,嘗試加入G2所在P的runnext(runnext是線程下一個(gè)需要執(zhí)行的 Goroutine。), 然后再是P的本地隊(duì)列和全局隊(duì)列。
系統(tǒng)調(diào)用阻塞
當(dāng)M執(zhí)行某一個(gè)G時(shí)候如果發(fā)生了阻塞操作,M會(huì)阻塞,如果當(dāng)前有一些G在執(zhí)行,調(diào)度器會(huì)把這個(gè)線程M從P中摘除,然后再創(chuàng)建一個(gè)新的操作系統(tǒng)的線程(如果有空閑的線程可用就復(fù)用空閑線程)來(lái)服務(wù)于這個(gè)P。當(dāng)M系統(tǒng)調(diào)用結(jié)束時(shí)候,這個(gè)G會(huì)嘗試獲取一個(gè)空閑的P執(zhí)行,并放入到這個(gè)P的本地隊(duì)列。如果獲取不到P,那么這個(gè)線程M變成休眠狀態(tài), 加入到空閑線程中,然后這個(gè)G會(huì)被放入全局隊(duì)列中。
隊(duì)列輪轉(zhuǎn)
可見每個(gè)P維護(hù)著一個(gè)包含G的隊(duì)列,不考慮G進(jìn)入系統(tǒng)調(diào)用或IO操作的情況下,P周期性的將G調(diào)度到M中執(zhí)行,執(zhí)行一小段時(shí)間,將上下文保存下來(lái),然后將G放到隊(duì)列尾部,然后從隊(duì)列中重新取出一個(gè)G進(jìn)行調(diào)度。
除了每個(gè)P維護(hù)的G隊(duì)列以外,還有一個(gè)全局的隊(duì)列,每個(gè)P會(huì)周期性地查看全局隊(duì)列中是否有G待運(yùn)行并將其調(diào)度到M中執(zhí)行,全局隊(duì)列中G的來(lái)源,主要有從系統(tǒng)調(diào)用中恢復(fù)的G。之所以P會(huì)周期性地查看全局隊(duì)列,也是為了防止全局隊(duì)列中的G被餓死。
除了每個(gè)P維護(hù)的G隊(duì)列以外,還有一個(gè)全局的隊(duì)列,每個(gè)P會(huì)周期性地查看全局隊(duì)列中是否有G待運(yùn)行并將其調(diào)度到M中執(zhí)行,全局隊(duì)列中G的來(lái)源,主要有從系統(tǒng)調(diào)用中恢復(fù)的G。之所以P會(huì)周期性地查看全局隊(duì)列,也是為了防止全局隊(duì)列中的G被餓死。
M0
M0是啟動(dòng)程序后的編號(hào)為0的主線程,這個(gè)M對(duì)應(yīng)的實(shí)例會(huì)在全局變量rutime.m0中,不需要在heap上分配,M0負(fù)責(zé)執(zhí)行初始化操作和啟動(dòng)第一個(gè)G,在之后M0就和其他的M一樣了
G0
G0是每次啟動(dòng)一個(gè)M都會(huì)第一個(gè)創(chuàng)建的goroutine,G0僅用于負(fù)責(zé)調(diào)度G,G0不指向任何可執(zhí)行的函數(shù),每個(gè)M都會(huì)有一個(gè)自己的G0,在調(diào)度或系統(tǒng)調(diào)用時(shí)會(huì)使用G0的棧空間,全局變量的G0是M0的G0
一個(gè)G由于調(diào)度被中斷,此后如何恢復(fù)?
中斷的時(shí)候?qū)⒓拇嫫骼锏臈P畔ⅲ4娴阶约旱腉對(duì)象里面。當(dāng)再次輪到自己執(zhí)行時(shí),將自己保存的棧信息復(fù)制到寄存器里面,這樣就接著上次之后運(yùn)行了。
我這里只是根據(jù)自己的理解進(jìn)行了簡(jiǎn)單的介紹,想要詳細(xì)了解有關(guān)GMP的底層原理可以去看Go調(diào)度器 G-P-M 模型的設(shè)計(jì)者的文檔或直接看源碼
參考: ()
()
就目前來(lái)看還是很有前景,因?yàn)樵絹?lái)越火了,不過(guò)他的應(yīng)用領(lǐng)域還是局限在高并發(fā)處理和網(wǎng)站開發(fā),畢竟是后起之秀所以在其他桌面程序領(lǐng)域沒那么容易普及和超越c++,找工作就不推薦學(xué)go
Go 中的分片數(shù)組,實(shí)際上有點(diǎn)類似于Java中的ArrayList,是一個(gè)可以擴(kuò)展的數(shù)組,但是Go中的切片由比較靈活,它和數(shù)組很像,也是基于數(shù)組,所以在了解Go切片前我們先了解下數(shù)組。
數(shù)組簡(jiǎn)單描述就由相同類型元素組成的數(shù)據(jù)結(jié)構(gòu), 在創(chuàng)建初期就確定了長(zhǎng)度,是不可變的。
但是Go的數(shù)組類型又和C與Java的數(shù)組類型不一樣, NewArray 用于創(chuàng)建一個(gè)數(shù)組,從源碼中可以看出最后返回的是 Array{}的指針,并不是第一個(gè)元素的指針,在Go中數(shù)組屬于值類型,在進(jìn)行傳遞時(shí),采取的是值傳遞,通過(guò)拷貝整個(gè)數(shù)組。Go語(yǔ)言的數(shù)組是一種有序的struct。
Go 語(yǔ)言的數(shù)組有兩種不同的創(chuàng)建方式,一種是顯示的初始化,一種是隱式的初始化。
注意一定是使用 [...]T 進(jìn)行創(chuàng)建,使用三個(gè)點(diǎn)的隱式創(chuàng)建,編譯器會(huì)對(duì)數(shù)組的大小進(jìn)行推導(dǎo),只是Go提供的一種語(yǔ)法糖。
其次,Go中數(shù)組的類型,是由數(shù)值類型和長(zhǎng)度兩個(gè)一起確定的。[2]int 和 [3]int 不是同一個(gè)類型,不能進(jìn)行傳參和比較,把數(shù)組理解為類型和長(zhǎng)度兩個(gè)屬性的結(jié)構(gòu)體,其實(shí)就一目了然了。
Go中的數(shù)組屬于值類型,通常應(yīng)該存儲(chǔ)于棧中,局部變量依然會(huì)根據(jù)逃逸分析確定存儲(chǔ)棧還是堆中。
編譯器對(duì)數(shù)組函數(shù)中做兩種不同的優(yōu)化:
在靜態(tài)區(qū)完成賦值后復(fù)制到棧中。
總結(jié)起來(lái),在不考慮逃逸分析的情況下,如果數(shù)組中元素的個(gè)數(shù)小于或者等于 4 個(gè),那么所有的變量會(huì)直接在棧上初始化,如果數(shù)組元素大于 4 個(gè),變量就會(huì)在靜態(tài)存儲(chǔ)區(qū)初始化然后拷貝到棧上。
由于數(shù)組是值類型,那么賦值和函數(shù)傳參操作都會(huì)復(fù)制整個(gè)數(shù)組數(shù)據(jù)。
不管是賦值或函數(shù)傳參,地址都不一致,發(fā)生了拷貝。如果數(shù)組的數(shù)據(jù)較大,則會(huì)消耗掉大量?jī)?nèi)存。那么為了減少拷貝我們可以主動(dòng)的傳遞指針呀。
地址是一樣的,不過(guò)傳指針會(huì)有一個(gè)弊端,從打印結(jié)果可以看到,指針地址都是同一個(gè),萬(wàn)一原數(shù)組的指針指向更改了,那么函數(shù)里面的指針指向都會(huì)跟著更改。
同樣的我們將數(shù)組轉(zhuǎn)換為切片,通過(guò)傳遞切片,地址是不一樣的,數(shù)組值相同。
切片是引用傳遞,所以它們不需要使用額外的內(nèi)存并且比使用數(shù)組更有效率。
所以,切片屬于引用類型。
通過(guò)這種方式可以將數(shù)組轉(zhuǎn)換為切片。
中間不加三個(gè)點(diǎn)就是切片,使用這種方式創(chuàng)建切片,實(shí)際上是先創(chuàng)建數(shù)組,然后再通過(guò)第一種方式創(chuàng)建。
使用make創(chuàng)建切片,就不光編譯期了,make創(chuàng)建切片會(huì)涉及到運(yùn)行期。1. 切片的大小和容量是否足夠小;
切片是否發(fā)生了逃逸,最終在堆上初始化。如果切片小的話會(huì)先在棧或靜態(tài)區(qū)進(jìn)行創(chuàng)建。
切片有一個(gè)數(shù)組的指針,len是指切片的長(zhǎng)度, cap指的是切片的容量。
cap是在初始化切片是生成的容量。
發(fā)現(xiàn)切片的結(jié)構(gòu)體是數(shù)組的地址指針array unsafe.Pointer,而Go中數(shù)組的地址代表數(shù)組結(jié)構(gòu)體的地址。
slice 中得到一塊內(nèi)存地址,array[0]或者unsafe.Pointer(array[0])。
也可以通過(guò)地址構(gòu)造切片
nil切片:指的unsafe.Pointer 為nil
空切片:
創(chuàng)建的指針不為空,len和cap為空
當(dāng)一個(gè)切片的容量滿了,就需要擴(kuò)容了。怎么擴(kuò),策略是什么?
如果原來(lái)數(shù)組切片的容量已經(jīng)達(dá)到了最大值,再想擴(kuò)容, Go 默認(rèn)會(huì)先開一片內(nèi)存區(qū)域,把原來(lái)的值拷貝過(guò)來(lái),然后再執(zhí)行 append() 操作。這種情況對(duì)現(xiàn)數(shù)組的地址和原數(shù)組地址不相同。
從上面結(jié)果我們可以看到,如果用 range 的方式去遍歷一個(gè)切片,拿到的 Value 其實(shí)是切片里面的值拷貝,即淺拷貝。所以每次打印 Value 的地址都不變。
由于 Value 是值拷貝的,并非引用傳遞,所以直接改 Value 是達(dá)不到更改原切片值的目的的,需要通過(guò) slice[index] 獲取真實(shí)的地址。
Go語(yǔ)言于2009年11月正式宣布推出,成為開放源代碼項(xiàng)目,并在Linux及Mac OS X平臺(tái)上進(jìn)行了實(shí)現(xiàn),后追加Windows系統(tǒng)下的實(shí)現(xiàn)。
谷歌資深軟件工程師羅布·派克(Rob Pike)表示,“Go讓我體驗(yàn)到了從未有過(guò)的開發(fā)效率。”派克表示,和今天的C++或C一樣,Go是一種系統(tǒng)語(yǔ)言。他解釋道,“使用它可以進(jìn)行快速開發(fā),同時(shí)它還是一個(gè)真正的編譯語(yǔ)言,我們之所以現(xiàn)在將其開源,原因是我們認(rèn)為它已經(jīng)非常有用和強(qiáng)大。”
2007年,谷歌把Go作為一個(gè)20%項(xiàng)目開始研發(fā),即讓員工抽出本職工作之外時(shí)間的20%,投入在該項(xiàng)目上。除了派克外,該項(xiàng)目的成員還有其它一些谷歌工程師。
派克表示,編譯后Go代碼的運(yùn)行速度與C語(yǔ)言非常接近,而且編譯速度非常快,就像在使用一個(gè)交互式語(yǔ)言。
現(xiàn)有編程語(yǔ)言均未專門對(duì)多核處理器進(jìn)行優(yōu)化。派克表示,Go就是谷歌工程師為這類程序編寫的一種語(yǔ)言。它不是針對(duì)編程初學(xué)者設(shè)計(jì)的,但學(xué)習(xí)使用它也不是非常困難。Go支持面向?qū)ο螅揖哂姓嬲姆庋b(closures)和反射(reflection)等功能。
在學(xué)習(xí)曲線方面,派克認(rèn)為Go與Java類似,對(duì)于Java開發(fā)者來(lái)說(shuō),應(yīng)該能夠輕松學(xué)會(huì)Go。
之所以將Go作為一個(gè)開源項(xiàng)目發(fā)布,目的是讓開源社區(qū)有機(jī)會(huì)創(chuàng)建更好的工具來(lái)使用該語(yǔ)言,例如Eclipse IDE中的插件。目前還沒有支持Go的IDE。
在目前谷歌公開發(fā)布的所有網(wǎng)絡(luò)應(yīng)用中,均沒有使用Go。但是谷歌已經(jīng)使用該語(yǔ)言開發(fā)了幾個(gè)內(nèi)部項(xiàng)目。
派克表示,Go是否會(huì)對(duì)谷歌即將推出的Chrome OS產(chǎn)生影響,現(xiàn)在還言之尚早,不過(guò)Go的確可以和Native Client配合使用。他表示,“Go可以讓應(yīng)用完美的運(yùn)行在瀏覽器內(nèi)。”例如,使用Go可以更高效的實(shí)現(xiàn)Wave,無(wú)論是在前端還是后臺(tái)。
Go語(yǔ)言是一種新的語(yǔ)言,一種并發(fā)的、帶垃圾回收的、快速編譯的語(yǔ)言。它具有以下特點(diǎn):
1.它可以在一臺(tái)計(jì)算機(jī)上用幾秒鐘的時(shí)間編譯一個(gè)大型的Go程序。
2.Go語(yǔ)言為軟件構(gòu)造提供了一種模型,它使依賴分析更加容易,且避免了大部分C風(fēng)格include文件與庫(kù)的開頭。
3.Go語(yǔ)言是靜態(tài)類型的語(yǔ)言,它的類型系統(tǒng)沒有層級(jí)。因此用戶不需要在定義類型之間的關(guān)系上花費(fèi)時(shí)間,這樣感覺起來(lái)比典型的面向?qū)ο笳Z(yǔ)言更輕量級(jí)。
4.Go語(yǔ)言完全是垃圾回收型的語(yǔ)言,并為并發(fā)執(zhí)行與通信提供了基本的支持。
按照其設(shè)計(jì),Go打算為多核機(jī)器上系統(tǒng)軟件的構(gòu)造提供一種方法。
Go語(yǔ)言是一種編譯型語(yǔ)言,它結(jié)合了解釋型語(yǔ)言的游刃有余,動(dòng)態(tài)類型語(yǔ)言的開發(fā)效率,以及靜態(tài)類型的安全性。它也打算成為現(xiàn)代的,支持網(wǎng)絡(luò)與多核計(jì)算的語(yǔ)言。要滿足這些目標(biāo),需要解決一些語(yǔ)言上的問(wèn)題:一個(gè)富有表達(dá)能力但輕量級(jí)的類型系統(tǒng),并發(fā)與垃圾回收機(jī)制,嚴(yán)格的依賴規(guī)范等等。這些無(wú)法通過(guò)庫(kù)或工具解決好,因此Go也就應(yīng)運(yùn)而生了。
Go 由于不支持泛型而臭名昭著,但最近,泛型已接近成為現(xiàn)實(shí)。Go 團(tuán)隊(duì)實(shí)施了一個(gè)看起來(lái)比較穩(wěn)定的設(shè)計(jì)草案,并且正以源到源翻譯器原型的形式獲得關(guān)注。本文講述的是泛型的最新設(shè)計(jì),以及如何自己嘗試泛型。
例子
FIFO Stack
假設(shè)你要?jiǎng)?chuàng)建一個(gè)先進(jìn)先出堆棧。沒有泛型,你可能會(huì)這樣實(shí)現(xiàn):
type?Stack?[]interface{}func?(s?Stack)?Peek()?interface{}?{
return?s[len(s)-1]
}
func?(s?*Stack)?Pop()?{
*s?=?(*s)[:
len(*s)-1]
}
func?(s?*Stack)?Push(value?interface{})?{
*s?=?
append(*s,?value)
}
但是,這里存在一個(gè)問(wèn)題:每當(dāng)你 Peek 項(xiàng)時(shí),都必須使用類型斷言將其從 interface{} 轉(zhuǎn)換為你需要的類型。如果你的堆棧是 *MyObject 的堆棧,則意味著很多 s.Peek().(*MyObject)這樣的代碼。這不僅讓人眼花繚亂,而且還可能引發(fā)錯(cuò)誤。比如忘記 * 怎么辦?或者如果您輸入錯(cuò)誤的類型怎么辦?s.Push(MyObject{})` 可以順利編譯,而且你可能不會(huì)發(fā)現(xiàn)到自己的錯(cuò)誤,直到它影響到你的整個(gè)服務(wù)為止。
通常,使用 interface{} 是相對(duì)危險(xiǎn)的。使用更多受限制的類型總是更安全,因?yàn)榭梢栽诰幾g時(shí)而不是運(yùn)行時(shí)發(fā)現(xiàn)問(wèn)題。
泛型通過(guò)允許類型具有類型參數(shù)來(lái)解決此問(wèn)題:
type?Stack(type?T)?[]Tfunc?(s?Stack(T))?Peek()?T?{
return?s[len(s)-1]
}
func?(s?*Stack(T))?Pop()?{
*s?=?(*s)[:
len(*s)-1]
}
func?(s?*Stack(T))?Push(value?T)?{
*s?=?
append(*s,?value)
}
這會(huì)向 Stack 添加一個(gè)類型參數(shù),從而完全不需要 interface{}。現(xiàn)在,當(dāng)你使用 Peek() 時(shí),返回的值已經(jīng)是原始類型,并且沒有機(jī)會(huì)返回錯(cuò)誤的值類型。這種方式更安全,更容易使用。(譯注:就是看起來(lái)更丑陋,^-^)
此外,泛型代碼通常更易于編譯器優(yōu)化,從而獲得更好的性能(以二進(jìn)制大小為代價(jià))。如果我們對(duì)上面的非泛型代碼和泛型代碼進(jìn)行基準(zhǔn)測(cè)試,我們可以看到區(qū)別:
type?MyObject?struct?{
X?
int
}
var?sink?MyObjectfunc?BenchmarkGo1(b?*testing.B)?{
for?i?:=?0;?i??b.N;?i++?{
var?s?Stack
s.Push(MyObject{})
s.Push(MyObject{})
s.Pop()
sink?=?s.Peek().(MyObject)
}
}
func?BenchmarkGo2(b?*testing.B)?{
for?i?:=?0;?i??b.N;?i++?{
var?s?Stack(MyObject)
s.Push(MyObject{})
s.Push(MyObject{})
s.Pop()
sink?=?s.Peek()
}
}
結(jié)果:
BenchmarkGo1BenchmarkGo1-16?????12837528?????????87.0?ns/op???????48?B/op????????2?allocs/opBenchmarkGo2BenchmarkGo2-16?????28406479?????????41.9?ns/op???????24?B/op????????2?allocs/op
在這種情況下,我們分配更少的內(nèi)存,同時(shí)泛型的速度是非泛型的兩倍。
合約(Contracts)
上面的堆棧示例適用于任何類型。但是,在許多情況下,你需要編寫僅適用于具有某些特征的類型的代碼。例如,你可能希望堆棧要求類型實(shí)現(xiàn) String() 函數(shù)
我喜歡jetbrains系列的IDE+go插件。不過(guò)我要說(shuō)的是這個(gè)問(wèn)題主要看你的觀點(diǎn)如何。
說(shuō)eclipse:
構(gòu)建方式是使用go install 命令,每一次編譯運(yùn)行都是go install。這樣的好處就是如果你有很多的包,下載下來(lái)并沒有編譯,這樣每次編譯速度是很快的。而且(!)go install 符合go官方的項(xiàng)目結(jié)構(gòu),官方說(shuō)過(guò)了,一個(gè)go的項(xiàng)目應(yīng)該是以個(gè)gopath,包含src,pkg,bin三個(gè)主要目錄。所以說(shuō)go install個(gè)人認(rèn)為才是主要的go編譯方式。
說(shuō)eclipse的缺點(diǎn):
其實(shí)eclipse插件的go編譯方式,還有目錄結(jié)構(gòu),項(xiàng)目結(jié)構(gòu),都是非常完美的!!!!真的很完美!可是,他的代碼提示,太差件!大括號(hào)都不能自動(dòng)補(bǔ)全,gdb 32bit 64bit兼容問(wèn)題,eclipseC++ 沒有html js插件,需要手動(dòng)安裝,幾乎不能開箱即用。不過(guò)如果你是開發(fā)算法,數(shù)據(jù)處理,還是推薦eclipse的,畢竟其他都無(wú)關(guān)緊要。
說(shuō)jetbrains:
說(shuō)先說(shuō)clione肯定不適合,新建項(xiàng)目沒有向?qū)В瑢?dǎo)致改成go項(xiàng)目各種不開心,比如圖標(biāo)對(duì)于我來(lái)說(shuō)就無(wú)法接受go lib 不是小耗子~這是次要的,重要的是各個(gè)文件都是灰色的(沒有在cmake中包含的結(jié)果),然后說(shuō)剩下的,phpstorm這個(gè)不說(shuō)了,估計(jì)很少有人插件按在這里,webstorm,體驗(yàn)也不是很好,idea?體驗(yàn)很好,可是畢竟比較重,尤其是現(xiàn)在加入了自家的K啥玩意(無(wú)意冒犯,沒記住單詞)~可是話說(shuō)回來(lái),go跟C系列IDE配合才是最佳,跟java系列一點(diǎn)不搭關(guān)系,用idea似乎有點(diǎn)格格不入,但是!idea支持新建項(xiàng)目向?qū)В琹ib的圖標(biāo)也很清晰,最后還是選擇idea吧,期待clion的強(qiáng)大起來(lái)!
再說(shuō)jetbrains系列缺點(diǎn):
插件的構(gòu)建方式是go buiild 這個(gè)讓人很不爽,我們幾乎不確定會(huì)構(gòu)建到什么地方去,還要每次設(shè)置一下run配置。這個(gè)可能無(wú)關(guān)緊要,畢竟不是什么大的毛病,可是go build不能緩存.a文件,直接構(gòu)建的結(jié)果就是很多第三方包的情況下很慢!所以建議安裝包的時(shí)候手動(dòng)install 一下解決這個(gè)問(wèn)題。自帶代碼格式化,這個(gè)格式化跟go 格格不入,總的來(lái)說(shuō)就是蛋疼,心碎,菊花癢。
最后說(shuō)liteIDE:
輕量級(jí)IDE,我可以說(shuō)是國(guó)人GO偉大作品典范,然而默認(rèn)構(gòu)建也是go build,項(xiàng)目管理方式不符合go官方標(biāo)準(zhǔn)。代碼提示不能自動(dòng)導(dǎo)入(eclipse也不能),不過(guò)如果你的項(xiàng)目是以包為單位的,那么另當(dāng)別論。一定很不錯(cuò),畢竟是輕量級(jí)專門針對(duì)GO的IDE!
說(shuō)這些,其實(shí)還有很大一部分取決于你的項(xiàng)目是用vendor機(jī)制管理,還是godeps機(jī)制管理依賴關(guān)系。go不像java擁有強(qiáng)大的幾乎天下一統(tǒng)的maven(無(wú)意冒犯,暫不評(píng)價(jià)其他構(gòu)建套件)。
go沒有官方包倉(cāng)庫(kù)。
go沒有官方包管理工具。
go沒有官方自動(dòng)化構(gòu)建套件。
上面三個(gè)沒有是致命要害。導(dǎo)致民間各種百花齊放。
說(shuō)說(shuō)我的項(xiàng)目怎么管理
gpm 一個(gè)shell工具(windows下你可以用git的bash,或者cygwin~)
我是嚴(yán)格艷照官方推薦方式管理go項(xiàng)目,一個(gè)go項(xiàng)目一個(gè)gopath。系統(tǒng)的gopath只是為了安裝go命令,我沒有配置gobin,意義不大。
項(xiàng)目的依賴跟我的代碼包都在src下(非vendor)
vendor用來(lái)存放包的特殊依賴,發(fā)布項(xiàng)目直接把依賴包發(fā)布上去(公網(wǎng)管理則只上傳依賴關(guān)系文件 godeps文件)
資源文件等都放在src目錄同級(jí),編譯文件放在bin,引用直接../引用。
文章標(biāo)題:go語(yǔ)言分析007,go語(yǔ)言分析tcpip協(xié)議
文章出自:http://chinadenli.net/article29/dsgghjh.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供小程序開發(fā)、網(wǎng)站設(shè)計(jì)、用戶體驗(yàn)、靜態(tài)網(wǎng)站、微信公眾號(hào)、響應(yīng)式網(wǎng)站
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)