package main import ( "fmt" "learner/Add" "time" ) //a. 普通類型,普通變量保存的就是值,也叫值類型 //b. 獲取普通變量的內存地址,用&,好比: var a int, 獲取a的內存地址:&a //c. 指針類型,指針變量存的就是一個內存地址,這個地址指向值 //d. 獲取指針類型所指向的值,使用:*,好比:var *p int, 使用*p獲取p指向的值 //e. 將一個內存地址給一個指針類型進行賦值(不能夠直接將變量賦值給指針,須要將內存地址賦值給指針): var a int=5, var p *int = &a // 在工程上有兩種最多見的併發通訊模型: 共享數據和消息通訊, go語言選擇後者,經過通訊進行共享內存 // channel, goroutine 間的通訊方式, 進程內的的通訊.進程間的通訊建議使用socket或者http等通訊協議. // channel 是類型相關的, 一個channel只能傳遞一種指定類型的值, 這個值須要在聲明channel時指定(能夠理解爲指定元素類型的管道) // 超時控制的經典實現 func chan_time_out_handler(ch chan int) (item bool){ // 使用 select 爲channel實現超時機制, select的一個case必須是一個面向channel的操做 // 定義一個time_out chan timeOut := make(chan bool, 1) go func(){ time.Sleep(1e9) // 等待一秒鐘 timeOut<- true }() // 利用time_out這個chan實現超時以後作何操做 select { case a := <- ch: // 嘗試從ch這個chan中讀取數據 fmt.Println(a) return true case <- timeOut: // 在等待時間範圍內一直沒有從ch中讀取到了數據可是從time_out 這個 chan 中讀取到了數據 return false } } // 只往chan中寫入數據 func chan_in(ch_in chan<- int) { for i:=0; i <= 10; i++{ ch_in <- 1 } // 若是是使用range遍歷chan, 那麼當chan關閉時, 讀取操做會當即結束,跳出循環(注意,這是channel中可能仍會存在數據), // channel關閉後,其實仍然能夠從中讀取已發送的數據(使用range沒法實現, 可使用常規的循環讀取channel的方式),讀取完數據後,將讀取到零值,能夠屢次讀取(仍然是零值) close(ch_in) // 當多個goroutine都使用了同一個channel時, 任何一個goroutine 中關閉了這個了這個channel, 其餘goroutine將沒法繼續對這個channel進行讀取 } // 只從chan讀出數據 func chan_out(ch_out <-chan int) { // 使用range, 當這個channel關閉時,就會跳出循環,可是channel裏面仍然可能存在數據 // x, ok := <- ch_out, 若是ok返回的是false,那麼就表示這個chan已經關閉 for value := range ch_out{ fmt.Printf("+++++++++++++++++%d", value) } } func main() { // 切片和map 都是指針類型的數據, 指針類型的數據均可以使用make()進行分配內存 chs := make([]chan int, 10) // 定義一個切片並分配內存, 元素是chan類型, 這個chan內能夠保存的元素爲int類型, 該切片初始內存能夠保存10個元素(不是channel的緩衝區, 是切片的初始大小) for i := 0; i < 10; i++ { chs[i] = make(chan int, 3) // 定義一個chan並分配內存, 緩衝區大小爲3, 而後保存到切片中, 若是不設置緩衝區,當寫入一個元素時,若是這個元素不被讀取掉,寫操做將會被阻塞 go Add.TestAddTwo(chs[i]) // 開啓協程發送chan } for _, ch := range (chs) { fmt.Println("====================", len(ch)) //當程序運行到這裏時, 這個channel有可能並無寫入數據,因此長度有可能爲0 1 2 a := <-ch // 當這裏從當前channel讀取不到數據時就會阻塞 // b := <-ch // 繼續讀取, 讀取不到就堵塞 fmt.Println(a) item := chan_time_out_handler(ch) fmt.Println(item) // 當channel寫完數據操做完成後若是沒有關閉,讀取完數據,chan爲空時,將會阻塞, 從而有可能形成死鎖, 因此chan使用完必須關閉 } // 單向channel的實現,當須要將一個單向的channel從讀變爲寫,或者從寫變爲讀時,須要進行類型轉換 // 第一個步,定義一個正常的channel ch_normal := make(chan int) // 第二步進行類型轉換,將ch_normal 轉換爲只容許寫的channel var ch_in chan<- int = ch_normal go chan_in(ch_in) // 第三步 生成一個只容許進行讀的channel var ch_out <-chan int = ch_normal chan_out(ch_out) } // 當向一個channel寫入數據, 在這個channel被讀取前, 這個操做是阻塞的(在緩衝區寫滿以前, 即便沒有讀取操做,寫操做都不會阻塞) // 當從一個channel讀取數據時,在對應的channel寫入數據前, 這個操做也是阻塞的,從而能夠利用channel實現了相似鎖的功能, 進而保證 // 了全部goroutine完成後主函數才返回 // 緩衝區滿以後將會阻塞,除非有goroutine對其進行操做, 不然協程就會停留在向該channel寫入元素的步驟上, 直到主進程退出, 向channel寫入數據的協程也就退出. 協程的阻塞不影響主進程的執行 // 定義一個channel var chanName chan ElementType // 多層定義,例如定義一個 map, 鍵是string類型,元素是bool類型的channel: var myMap map[string] chan bool // 聲明之後,定義一個channel 並賦值給變量: map["firstChan"] := make(chan false) , 使用內建函數make() // 若是是使用range遍歷chan, 那麼當chan關閉時, 讀取操做會當即結束,跳出循環(注意,這是channel中可能仍會存在數據) // 當多個goroutine都使用了同一個channel時, 任何一個goroutine 中關閉了這個了這個channel, // 其餘goroutine將沒法繼續對這個channel進行讀取, 能夠在主進程中進行守護, 等全部的goroutine執行完畢後再去關閉channel // close(chan) //關閉一個channel // 判斷一個channel是否已關閉 //1. 若是channel已經關閉,繼續往它發送數據會致使panic: send on closed channel //2. 關閉一個已經關閉的channel也會致使panic: close of closed channel func test2(ch chan int){ for{ if value,ok:=<-ch;ok{ //do somthing fmt.Print(value) }else{ break //ok 爲false, 表示channel已經被關閉,退出循環 } } } // channel關閉後,仍然能夠從中讀取已發送的數據(使用range沒法實現),讀取完數據後,將讀取到零值,能夠屢次讀取。 func test1(){ ch:=make(chan int,3) ch<-3 ch<-2 ch<-1 close(ch) fmt.Print(<-ch) fmt.Print(<-ch) fmt.Print(<-ch) fmt.Print(<-ch) fmt.Print(<-ch) }