一、死鎖
* 在主goroutine中向無(wú)緩存channel添加內(nèi)容或在主goroutine中向channel添加內(nèi)容且添加內(nèi)容的個(gè)數(shù)已經(jīng)大于channel緩存?zhèn)€數(shù)就會(huì)產(chǎn)生死鎖
?? ?fatal error : all goroutines are asleep -deadlock!
* 死鎖:在程序中多個(gè)進(jìn)程(Golang中g(shù)oroutine)由于相互競(jìng)爭(zhēng)資源而產(chǎn)生的阻塞(等待)狀態(tài),而這種狀態(tài)一直保持下去,此時(shí)稱這個(gè)線程是死鎖狀態(tài)
* 在Golang中使用無(wú)緩存channel時(shí)一定要注意.以下是一個(gè)最簡(jiǎn)單的死鎖程序
????* 主協(xié)程中有ch<-1,無(wú)緩存channel無(wú)論添加還是取出數(shù)據(jù)都會(huì)阻塞goroutine,當(dāng)前程序無(wú)其他代碼,主goroutine會(huì)一直被阻塞下去,此時(shí)主goroutine就是死鎖狀態(tài)
?? ?func main() {
???? ??ch := make(chan int)
???? ??ch <- 1
?? ?}
* 而下面代碼就不會(huì)產(chǎn)生死鎖
????* 通過(guò)代碼示例可以看出,在使用無(wú)緩存channel時(shí),特別要注意的是在主協(xié)程中要操作channel代碼
?? ?func main() {
????? ?ch := make(chan int)
????? ?go func() {
???????? ?ch <- 1
???????? ?fmt.Println("執(zhí)行g(shù)oroutine")
????? ?}()
????? ?time.Sleep(5e9)
????? ?fmt.Println("程序執(zhí)行結(jié)束")
?? ?}
二、有緩存通道
* 創(chuàng)建一個(gè)有緩存通道
func main() {
???ch := make(chan int, 3) //緩存大小3,里面消息個(gè)數(shù)小于等于3時(shí)都不會(huì)阻塞goroutine
???ch <- 1
???ch <- 2
???ch <- 3
???ch <- 4 //此行出現(xiàn)死鎖,超過(guò)緩存大小數(shù)量
}
* 在Golang中有緩存channel的緩存大小是不能改變的,但是只要不超過(guò)緩存數(shù)量大小,都不會(huì)出現(xiàn)阻塞狀態(tài)
?? ?func main() {
????? ?ch := make(chan int, 3) //緩存大小3,里面消息個(gè)數(shù)小于等于3時(shí)都不會(huì)阻塞goroutine
????? ?ch <- 1
????? ?fmt.Println(<-ch)
???? ??ch <- 2
????? ?fmt.Println(<-ch)
????? ?ch <- 3
????? ?ch <- 4
???? ??fmt.Println(len(ch))//輸出2,表示channel中有兩個(gè)消息
???? ??fmt.Println(cap(ch))//輸出3,表示緩存大小總量為3
?? ?}
三、select簡(jiǎn)介
* Golang中select和switch結(jié)構(gòu)特別像,但是select中case的條件只能是I/O
* select 的語(yǔ)法(condition是條件)
?? ?select{
??? ??case condition:
??? ??case condition:
??? ??default:
?? ?}
* select執(zhí)行過(guò)程:
????* 每個(gè)case必須是一個(gè)IO操作
????* 哪個(gè)case可以執(zhí)行就執(zhí)行哪個(gè)
????* 多個(gè)case都可以執(zhí)行,隨機(jī)執(zhí)行一個(gè)
????* 所有case都不能執(zhí)行時(shí),執(zhí)行default
????* 所有case都不能執(zhí)行,且沒(méi)有default,將會(huì)阻塞
func main() {
???runtime.GOMAXPROCS(1)
???ch1 := make(chan int, 1)
???ch2 := make(chan string, 1)
???ch1 <- 1
???ch2 <- "hello"
???select {
???case value := <-ch1:
??????fmt.Println(value)
???case value := <-ch2:
??????fmt.Println(value)
???}
}
* select多和for循環(huán)結(jié)合使用,下面演示出了一直在接收消息的例子
func main() {
????ch := make(chan int)
????for i := 1; i <= 5; i++ {
????????go func(arg int) {
????????????ch <- arg
????????}(i)
????}
??//如果是一直接受消息,應(yīng)該是死循環(huán)for{},下面代碼中是明確知道消息個(gè)數(shù)
????for i := 1; i <= 5; i++ {
????????select {
????????case c := <-ch:
????????????fmt.Println("取出數(shù)據(jù)", c)
????????default:
????????????//沒(méi)有default會(huì)出現(xiàn)死鎖
????????}
????}
????fmt.Println("程序執(zhí)行結(jié)束")
}
* break可以對(duì)select生效,如果for中嵌套select,break選擇最近結(jié)構(gòu)
四、GC
* GC英文全稱 garbage collector
* Go語(yǔ)言GC是相對(duì)C/C++語(yǔ)言非常重要的改進(jìn)
* 一些常用GC算法
????* 引用計(jì)算法:當(dāng)對(duì)象被引用時(shí)計(jì)算器加一.不被引用計(jì)數(shù)器減一
????????* PHP和Object-C使用
????????* 相互引用無(wú)法回收
????????* 計(jì)數(shù)增加消耗
????* Mark And Sweep 標(biāo)記和清除算法:停止程序運(yùn)行,遞歸遍歷對(duì)象,進(jìn)行標(biāo)記。標(biāo)記完成后將所有沒(méi)有引用的對(duì)象進(jìn)行清除
????????* 由于標(biāo)記需要停止程序(Stop the world),當(dāng)對(duì)象特別多時(shí),標(biāo)記和清除過(guò)程比較耗時(shí)(可能幾百毫秒),很難接受
????* 三色標(biāo)記法:是Mark And Sweep的改進(jìn)版.從邏輯上分為白色區(qū)(未搜索),灰色區(qū)(正搜索),黑色區(qū)(已搜索).灰色區(qū)內(nèi)容是子引用沒(méi)有進(jìn)行搜索,黑色區(qū)表示子引用存在
????* 分代收集:一般情況都有三代,例如java中新生代,老年代,永久代.當(dāng)新生代中帶有閾值時(shí)會(huì)把對(duì)象放入到老年代,相同道理老年代內(nèi)容達(dá)到閾值會(huì)放入到永久代
五、Go語(yǔ)言中的GC
* Go語(yǔ)言中采用Stop The World方式
* Golang每個(gè)版本基本上都會(huì)對(duì)GC進(jìn)行優(yōu)化,從Golang1.5開始支持并發(fā)(concurrent )收集,從1.8版本已經(jīng)把STW時(shí)間優(yōu)化到了100微妙,通常只需要10微秒以下.且在1.10版本時(shí)再次優(yōu)化減少GC對(duì)CPU占用
* Go語(yǔ)言中GC是自動(dòng)運(yùn)行的,在下列情況下會(huì)觸發(fā)GC
????* 當(dāng)需要申請(qǐng)內(nèi)存時(shí),發(fā)現(xiàn)GC是上次GC兩倍時(shí)會(huì)觸發(fā)
????* 每2分鐘自動(dòng)運(yùn)行一次GC
* GC調(diào)優(yōu)
????* 小對(duì)象復(fù)用,局部變量盡量少聲明,多個(gè)小對(duì)象可以放入到結(jié)構(gòu)體,方便GC掃描
????* 少用string的”+”