1、管道分類
- 讀寫管道
- 只讀管道
- 只寫管道
- 緩沖通道 :創建時指定大小(如果不指定默認為非緩沖通道)
2、正確使用管道
-
管道關閉后自能讀,不能寫
-
寫入管道不能超過管道的容量cap,滿容量還寫則會阻塞
-
管道為空時,如果沒有關閉,則繼續讀取會阻塞當前線程,直到有東西寫入管道
-
m,ok:=<-intChan //ok用來檢測產是否已經關閉 false代表關閉了,關閉了m就是默認值
一般如下操作才可以判斷讀取是否完畢,如果寫進程沒有關閉管道則說明還有東西要寫
v,ok:=<-intChan if !ok{ fmt.Println("讀取完畢") break }
3、管道遍歷與訪問
- for-range訪問
- select訪問(常用)
for-range
//gor-range 如果管道沒有數據同時還沒有關閉 則會一直阻塞等待
go func() {
for i:=range c{ //注意這里沒有ok判斷是否關閉了 只有一個返回值
fmt.Println(i)
}
}()
下面這種情況for-range會阻塞,1秒后打印1,和 “關閉了”,如果沒寫同時又沒有關閉,那么則會一直阻塞
ch := make(chan int)
go func() {
time.Sleep(time.Second)
//close(ch)
ch<-1
}()
go func() {
for i:=range ch{
log.Println(i)
}
log.Println("關閉了")
}()
time.Sleep(time.Hour)
下面的這種情況,for-range在一秒后會結束 ,同時打印“關閉了”
ch := make(chan int)
go func() {
time.Sleep(time.Second)
close(ch)
//ch<-1
}()
go func() {
for i:=range ch{
log.Println(i)
}
log.Println("關閉了")
}()
time.Sleep(time.Hour)
select
select會隨機選擇case里面沒有阻塞的管道進行讀取或寫入,如果都阻塞則執行default語句,一般select都會伴隨一個default語句
c:=make(chan string,2)
send:= func(v string) {
select {
case c<-v:log.Println("輸入",v)
default:log.Println("緩沖區已經滿")
}
}
receive:= func() string {
select {
case v:=<-c:return v
default:
log.Println("緩沖區空")
return ""
}
}
send("h1")
send("h2")
send("h3") //輸入失敗
log.Println(receive())
log.Println(receive())
log.Println(receive()) //取失敗
當管道關閉時,select也會執行成功,如下打印了一秒‘等' 之后就會結束,這里沒有往管道寫東西,所以結束時打印的是默認值0
ch := make(chan int)
//go func() {
// ch <- 1
//}()
go func() {
time.Sleep(time.Second)
close(ch)
}()
go func() {
for {
time.Sleep(time.Microsecond*500)
select {
case v := <-ch:
log.Println(v)
return
default:
log.Println("等")
}
}
}()
time.Sleep(time.Hour)
4、阻塞情況
1、沒有關閉,同時管道元素為0,讀進程阻塞
2、沒有關閉,同時管道元素已經滿,則寫進程阻塞,無緩沖管道的話則會阻塞在寫,直到有人讀
3、關閉管道,讀光了還讀,則讀取出來的是對應類型管道的默認空值,在一個關閉通道進行寫操作會報錯
ch:=make(chan int,1)
ch<-1
close(ch)
log.Println(<-ch) //1
log.Println(<-ch) //0
log.Println(<-ch)//0
a,ok:=<-ch
log.Println(a,ok)//0 false
-
關閉管道,還寫,則報錯
-
無緩沖=堵塞,緩沖=非堵塞 無緩沖是同步,有緩沖是異步
像下面這個無緩沖管道,沒人讀則一直阻塞,只會打印A1,有一個管道讀走了才會繼續執行寫操作,就比如快遞員來發快遞,只會等你拿走快遞才會離開,否則一直在等你來拿
ch := make(chan int)
go func() {
for{
log.Println("A1")
time.Sleep(time.Second)
//close(ch)
ch<-1
log.Println("A2")
}
}()
那如果我把cap設置成1呢?
ch := make(chan int,1)
go func() {
for{
log.Println("A1")
time.Sleep(time.Second)
//close(ch)
ch<-1
log.Println("A2")
}
}()
打印如下,可以寫一個
2020/03/21 20:02:59 A1
2020/03/21 20:03:00 A2
2020/03/21 20:03:00 A1
其他關於通道的使用可以看這兩篇博客
go通道的常見使用場景.
go通道的優雅關閉.