Channel的基本概念
Channal就是用來通信的,像Unix下的管道一樣,
它的操作符是箭頭" <-" , 箭頭的指向就是數(shù)據(jù)的流向
ch <- v // 發(fā)送值v到Channel ch中
v := <-ch // 從Channel ch中接收數(shù)據(jù),并將數(shù)據(jù)賦值給v
下面的程序演示了一個(gè)goroutine和主程序通信的例程。
package main
import "fmt"
func main() {
//創(chuàng)建一個(gè)string類型的channel
channel := make(chan string)
//創(chuàng)建一個(gè)goroutine向channel里發(fā)一個(gè)字符串
go func() { channel <- "hello" }()
msg := <- channel
fmt.Println(msg)
}
chan為先入先出的隊(duì)列,有三種類型,雙向,只讀,只寫,分別為"chan","chan<-","<-chan"
初始化時(shí)候,可以指定容量make(chanint,100);容量(capacity)代表Channel容納的最多的元素的數(shù)量
Channel的阻塞
channel默認(rèn)上是阻塞的,也就是說,如果Channel滿了,就阻塞寫,如果Channel空了,就阻塞讀。于是,我們就可以使用這種特性來同步我們的發(fā)送和接收端。
package main
import "fmt"
import "time"
func main() {
channel := make(chan string) //注意: buffer為1
go func() {
channel <- "hello"
fmt.Println("write \"hello\" done!")
channel <- "World" //Reader在Sleep,這里在阻塞
fmt.Println("write \"World\" done!")
fmt.Println("Write go sleep...")
time.Sleep(3*time.Second)
channel <- "channel"
fmt.Println("write \"channel\" done!")
}()
time.Sleep(2*time.Second)
fmt.Println("Reader Wake up...")
msg := <-channel
fmt.Println("Reader: ", msg)
msg = <-channel
fmt.Println("Reader: ", msg)
msg = <-channel //Writer在Sleep,這里在阻塞
fmt.Println("Reader: ", msg)
}
結(jié)果為
Reader Wake up...
Reader: hello
write "hello" done!
write "World" done!
Write go sleep...
Reader: World
write "channel" done!
Reader: channel
總結(jié)
- channel的定義
- 定義語法 :
- var ch =make(chan 通道中傳遞的數(shù)據(jù)類型,容量大小) 0為無緩沖,其余為有緩沖
- 讀channel:
- <-ch 讀到數(shù)據(jù)拋空,用來調(diào)節(jié)各個(gè)go程的先后順序
- num := <-ch 讀到數(shù)據(jù),存入num中
- 寫channel:
- ch <- data data類型嚴(yán)格于定義的語法一致
- 特性:
- 通道中的數(shù)據(jù)只能單向流動(dòng)。一端讀端,另一端必須寫端。
- 通道中的數(shù)據(jù)只能一次讀取,不能重復(fù)讀。
- 讀端和寫端在不同的go程之間
- 讀端讀,寫端不在寫,讀端阻塞。寫端寫,讀端不在線,寫端阻塞。
- 系統(tǒng)3個(gè)特殊文件:
- stdin:標(biāo)準(zhǔn)輸入文件 ---鍵盤
- stdout:標(biāo)準(zhǔn)輸出文件 ---屏幕
- stderr:標(biāo)準(zhǔn)錯(cuò)誤文件 ---屏幕
- 定義語法 :
- channel分類
- 無緩沖
- 要求 讀端寫端同時(shí)在線,對(duì)端不在線,本端阻塞
- 有緩沖
- 讀端,不在線,寫端可以將數(shù)據(jù)直接寫入緩沖區(qū)(不阻塞) 直到緩沖區(qū)被寫滿,依然沒有讀端,寫端阻塞
- 無緩沖
go程間通信
- 多個(gè)go程間,如果有多個(gè)共享資源時(shí),需要分別同步
同步通信,異步通信
- 同步通信:——無緩沖channel
- 一個(gè)調(diào)用發(fā)出,如果沒有得到結(jié)果,那么該調(diào)用不返回 ——阻塞
- 異步通信:
- 一個(gè)調(diào)用發(fā)出,不等待結(jié)果,直接返回——不阻塞
關(guān)閉channel
當(dāng)寫端,寫完數(shù)據(jù)后,使用close(ch)關(guān)閉channel
讀端,可以判斷channel是否可讀
for{ if num,ok:= <-ch;ok{ fmt.Println("num=",num)//讀ch中的數(shù)據(jù) }else{ break //跳出數(shù)據(jù) } } // 讀channel的內(nèi)容,結(jié)束后自動(dòng)跳出 // 是上面一種的簡(jiǎn)單寫法 for num := range ch{ }
-
關(guān)閉channel的特性:
- 如果寫端沒有關(guān)閉,暫停寫入,讀端阻塞等待
- 如果寫端已經(jīng)關(guān)閉,不能寫入,報(bào)錯(cuò):==panic:send on closed channel==
- 如果寫端已經(jīng)關(guān)閉,讀端依然能讀取,讀到的是數(shù)據(jù)類型的零值
單向channel
- 定義語法:
- 雙向channel:
- var ch chan 數(shù)據(jù)類型
- 單向讀channel:
- var chr <- chan int
- chr = ch 雙向channel給單向讀channel賦值
- 只能做讀操作不能做寫操作
- 單向?qū)慶hannel:
- var chw chan <- int
- chw = ch 雙向channel給單向?qū)慶hannel賦值
- 只能做寫操作不能做讀操作
- 雙向channel:
- 單向channel==不能==給雙向channel賦值
- 單向channel的使用:
- 主要應(yīng)用于函數(shù)傳參,單向channel可以在語法層面限定channel使用的方法
- 單向讀:不能寫
- 單向?qū)懀翰荒茏x
- 主要應(yīng)用于函數(shù)傳參,單向channel可以在語法層面限定channel使用的方法
Select關(guān)鍵字
多個(gè)Channel的select
package main
import "time"
import "fmt"
func main() {
//創(chuàng)建兩個(gè)channel - c1 c2
c1 := make(chan string)
c2 := make(chan string)
//創(chuàng)建兩個(gè)goruntine來分別向這兩個(gè)channel發(fā)送數(shù)據(jù)
go func() {
time.Sleep(time.Second * 1)
c1 <- "Hello"
}()
go func() {
time.Sleep(time.Second * 1)
c2 <- "World"
}()
//使用select來偵聽兩個(gè)channel
for i := 0; i < 2; i++ {
select {
case msg1 := <-c1:
fmt.Println("received", msg1)
case msg2 := <-c2:
fmt.Println("received", msg2)
}
}
}
select的使用
- 作用:用來監(jiān)聽channel上的數(shù)據(jù)流動(dòng)
- 特性:
- 每一個(gè)case分支,都必須是IO操作
- 通常將select置于for循環(huán)中
- 一個(gè)case監(jiān)聽的channel不滿足監(jiān)聽條件,當(dāng)前case分支阻塞
- 當(dāng)所有case分支都不滿足監(jiān)聽條件時(shí),select如果有default分支時(shí),走default,否則等待case滿足
- 當(dāng)監(jiān)聽的多個(gè)case分支中,同時(shí)滿足多個(gè)case,隨機(jī)選擇任意一個(gè)執(zhí)行
- 為防止忙輪詢出現(xiàn),可以適當(dāng)選擇省略default
- break關(guān)鍵字不能應(yīng)用于結(jié)束整個(gè)select,只能跳出一個(gè)分支
- ==結(jié)論==:所有使用select的go程,與其他go程間,通信時(shí),采用的是==異步通信==