(1)管道本質就是一個數據結構-隊列
(2)數據是先進先出
(3)自身線程安全,多協程訪問時,不需要加鎖,channel本身就是線程安全的
(4)管道有類型的,一個string的管道只能存放string類型數據
var 變量名 chan 數據類型
PS1:chan管道關鍵字
PS2:數據類型指的是管道的類型,里面放入數據的類型,管道是有類型的,int類型的管道只能寫入整數int
PS3:管道是引用類型,必須初始化才能寫入數據,即make后才能使用
func main() { //定義管道 、 聲明管道 ---> 定義一個int類型的管道 var intChan chan int //通過make初始化:管道可以存放3個int類型的數據 intChan = make(chan int, 3) //證明管道是引用類型: fmt.Printf("intChan的值: %v /n",intChan) //向管道存放數據: intChan <- -10 num := 20 intChan <- num intChan <- 40 //注意:不能存放大于容量的數據: // intChan <- -80 //輸出管道的長度: fmt.Printf("管道的實際長度:%v,管道的容量是:%v /n",len(intChan),cap(intChan)) //在管道中讀取數據: num1 := <-intChan num2 := <-intChan num3 := <-intChan fmt.Println(num1) fmt.Println(num2) fmt.Println(num3) //注意:在沒有使用協程的情況下,如果管道的數據已經全部取出,那么再取就會報錯: // num4 := <-intChan // fmt.Println(num4) fmt.Printf("管道的實際長度:%v,管道的容量是:%v /n",len(intChan),cap(intChan))}
使用內置函數close可以關閉管道,當管道關閉后,就不能再向管道寫數據了,但是仍然可以從該管道讀取數據。
func main() { var intChan chan int intChan = make(chan int, 3) intChan <- 10 intChan <- 20 //關閉管道: close(intChan) //再次寫入數據:--->報錯 // intChan <- 30 //當管道關閉后,讀取數據是可以的: num := <- intChan fmt.Println(num)}
管道支持for-range的方式進行遍歷,請注意兩個細節
1)在遍歷時,如果管道沒有關閉,則會出現deadlock的錯誤
2)在遍歷時,如果管道已經關閉,則會正常遍歷數據,遍歷完后,就會退出遍歷。
func main() { var intChan chan int intChan = make(chan int, 100) for i := 0; i < 100; i++ { intChan <- i } //在遍歷前,如果沒有關閉管道,就會出現deadlock的錯誤 //所以我們在遍歷前要進行管道的關閉 // for v := range intChan { // fmt.Println("value = ",v) // } close(intChan) //遍歷:for-range for v := range intChan { fmt.Println("value = ",v) }}
請完成協程和管道協同工作的案例,具體要求:
1) 開啟一個writeData協程,向管道中寫入50個整數.
2) 開啟一個readData協程,從管道中讀取writeData寫入的數據。
3) 注意: writeData和readDate操作的是同一個管道
4) 主線程需要等待writeData和readDate協程都完成工作才能退出
package mainimport ( "fmt" "time" "sync")var wg sync.WaitGroup//寫:func writeData(intChan chan int) { defer wg.Done() for i := 1; i <= 50; i++ { intChan <- i fmt.Println("寫入的數據為:",i) time.Sleep(time.Second) } close(intChan)}//讀:func readData(intChan chan int) { defer wg.Done() for v := range intChan { fmt.Println("讀取的數據為:",v) time.Sleep(time.Second) }}func main() { //主線程 //寫協程和讀協程共同操作同一個管道-》定義管道: intChan := make(chan int, 50) wg.Add(2) //開啟讀和寫的協程: go writeData(intChan) go readData(intChan) //主線程一直在阻塞,什么時候wg減為0了,就停止 wg.Wait() fmt.Println("讀寫數據完成...")}
運行結果:
package mainimport ( "fmt")func main() { //默認情況下,管道是雙向的--》可讀可寫: //聲明為只寫: // 管道具備<- 只寫性質 var intChan chan<- int intChan = make(chan int, 3) intChan <- 10 // 報錯 // num := <- intChan fmt.Println("intChan:",intChan) //聲明為只讀: // 管道具備<- 只讀性質 var intChan2 <-chan int if intChan2 != nil { num1 := <- intChan2 fmt.Println("num1:",num1) } // 報錯 // intChan2 <- 30}
package mainimport ( "fmt" "sync")var wg sync.WaitGroupfunc writeData(intChan chan int) { defer wg.Done() for i := 1; i < 10; i++ { intChan <- i fmt.Println("寫入的數據:",i) } close(intChan)}func readData(intChan chan int) { defer wg.Done() for v := range intChan { fmt.Println("讀取的數據為:",v) }}func main() { intChan := make(chan int, 10) wg.Add(2) go writeData(intChan) // go readData(intChan) wg.Wait()}
運行結果
package mainimport ( "fmt" "sync" "time")var wg sync.WaitGroupfunc writeData(intChan chan int) { defer wg.Done() for i := 1; i < 10; i++ { intChan <- i fmt.Println("寫入的數據:",i) } close(intChan)}func readData(intChan chan int) { defer wg.Done() for v := range intChan { fmt.Println("讀取的數據為:",v) time.Sleep(time.Second) }}func main() { intChan := make(chan int, 10) wg.Add(2) go writeData(intChan) go readData(intChan) wg.Wait()}
PS:case后面必須進行的是io操作,不能是等值,隨機去選擇一個io操作
PS:default防止select被阻塞住,加入default
package mainimport ( "fmt" "time")func main() { intChan := make(chan int, 1) go func () { time.Sleep(time.Second * 15) intChan <- 15 }() stringChan := make(chan string, 1) go func () { time.Sleep(time.Second * 12) stringChan <- "hellocyz" }() //本身取數據就是阻塞的 // fmt.Println(<-intChan) select { case v := <-intChan : fmt.Println("intChan:",v) case v := <-stringChan : fmt.Println("stringChan:",v) default: fmt.Println("防止select被阻塞") }}
package mainimport ( "fmt" "time")//輸出數字:func printNum() { for i := 1; i <= 10; i++ { fmt.Println(i) }}//做除法操作:func divide() { defer func () { err := recover() if err != nil { fmt.Println("devide()出現錯誤:",err) } }() num1 := 10 num2 := 0 result := num1 / num2 fmt.Println(result)}func main() { //啟動兩個協程: go printNum() go divide() time.Sleep(time.Second * 5)}
結果:
本文鏈接:http://www.tebozhan.com/showinfo-26-5170-0.html聊聊協程和管道—管道
聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com
上一篇: 如何使用Kafka構建事件驅動的架構?