Channel是Go中的一個核心類型,你能夠把它當作一個管道,經過它併發核心單元就能夠發送或者接收數據進行通信(communication)。面試
T表示任意的一種類型併發
單向的channel,不只能夠經過聲明make(chan <- interface{})
來建立,還能夠經過隱身或顯示的經過 chan
來轉換,以下函數
func main() { channel := make(chan int, 10) convert(channel) } func convert(channel chan<- int) {}
在 convert函數中,就能夠吧channel當成單向輸入管道來使用了
code
既然 雙向 chan,既能夠接收,也能夠發送,爲何還會有單向chan的存在? 個人一個理解即是 權限收斂,例如一個爬蟲系統中,有些進程a僅僅負責抓取頁面內容,並轉發給進程b,那進程a僅須要 單向發送的chan
便可進程
缺省狀況下,發送chan或接收chan會一直阻塞着,直到另外一方準備好。這種方式能夠用來在gororutine中進行同步,而沒必要使用顯示的鎖或者條件變量。同步
如官方的例子中x, y := <-c, <-c
這句會一直等待計算結果發送到channel中。如下面例子看一下io
func bufferChannel() { channel := make(chan int) i := 0 go func(i int) { fmt.Printf("start goroutine %d\n", i)① channel <- i fmt.Printf("send %d to channel\n", i)② }(i) time.Sleep(2 * time.Second) fmt.Println("sleep 2 second") value := <-channel③ fmt.Println("got ", value) }
輸出結果以下class
start goroutine 0 sleep 2 second got 0 send 0 to channel
能夠看出,go func
執行到了①後並無繼續執行②,而是等待③執行完成後,再去執行②,也就能夠說明 channel <- i
阻塞了goroutine
的繼續執行變量
若是,我不想在這裏阻塞,而是我直接把數據放到channel
裏,等接收方準備好後,到channel
中自取自用如何處理,這裏就涉及到了另外一個概念 buffered channelsed
咱們把程序修改一下
func bufferChannel() { channel := make(chan int, 1) // 這裏加了個參數 i := 0 go func(i int) { fmt.Printf("start goroutine %d\n", i)① channel <- i fmt.Printf("send %d to channel\n", i)② }(i) time.Sleep(2 * time.Second) fmt.Println("sleep 2 second") value := <-channel③ fmt.Println("got ", value) }
輸出結果
start goroutine 0 send 0 to channel sleep 2 second got 0
咱們發現go func
執行完①以後就執行了②,並無等待③的執行結束,這就是buffered channel的效果了
咱們只須要在make的時候,聲明底2個參數,也就是chan的緩衝區大小便可
經過上面的程序能夠看出,咱們一直在使用③的造成,即<- chan
來讀取chan中的數據,可是若是有多個goroutine在同時像一個chan寫數據,咱們除了使用
for { value <- chan }
還有什麼更優雅的方式嗎
仍是上面那個程序,咱們使用 for … range 進行一下改造
func bufferChannel() { channel := make(chan int, 1) i := 0 go func(i int) { fmt.Printf("start goroutine %d\n", i) channel <- i fmt.Printf("send %d to channel\n", i) }(i) time.Sleep(2 * time.Second) fmt.Println("sleep 2 second") for value := range channel { fmt.Println("got ", value) } }
這樣就能夠遍歷 channel
中的數據了,可是咱們在運行的時候就會發現,哎 這個程序怎麼停不下來了?range channel
產生的迭代值爲Channel中發送的值,它會一直迭代直到channel被關閉,因此 咱們goroutine
發送完數據後,把channel
關閉一下試試,這一次,咱們再也不進行time.Sleep(2 * time.Second)
func bufferChannel() { channel := make(chan int, 1) i := 0 go func(i int) { fmt.Printf("start goroutine %d\n", i) channel <- i fmt.Printf("send %d to channel\n", i) close(channel) }(i) for value := range channel { fmt.Println("got ", value) } }
這樣,整個程序就能夠正常退出了,因此,在使用range
的時候須要注意,若是channel
不關閉,則range
會一直阻塞在這裏的
咱們上面講的一直都是隻有一個channel
的時候,咱們應該怎麼去作,加入有兩個channel
或者更多的channel
,咱們應該怎麼去作,這裏就介紹一下 go裏面的多路複用 select
,如下面程序爲例
func example() { tick := time.Tick(time.Second) after := time.After(3 * time.Second) for { select { case <-tick: fmt.Println("tick 1 second") case <-after: fmt.Println("after 3 second") return default: fmt.Println("come into default") time.Sleep(500 * time.Millisecond) } } }
time.Tick
是go的time包提供的一個定時器的一個函數,它返回一個channel
,並在指定時間間隔內,向channel發送一條數據,time.Tick(time.Second)
就是每秒鐘向這個channel發送一個數據
time.After
是go的time包提供的一個定時器的一個函數,它返回一個channel
,並在指定時間間隔後,向channel發送一條數據,time.After(3 * time.Second)
就是3s後向這個channel發送一個數據
輸出結果
come into default come into default tick 1 second come into default come into default tick 1 second come into default come into default tick 1 second after 3 second
能夠看到,select
會選擇一個沒有阻塞的 channel
,並執行響應 case
下的邏輯,這樣就能夠避免因爲一個 channel
阻塞而致使後續的邏輯阻塞的狀況了
咱們繼續作個小實驗,把上面關閉的channel
放到 select
裏面試一下
func example() { tick := time.Tick(time.Second) after := time.After(3 * time.Second) channel := make(chan int, 1) go func() { channel <- 1 close(channel) }() for { select { case <-tick: fmt.Println("tick 1 second") case <-after: fmt.Println("after 3 second") return case value := <- channel: fmt.Println("got", value) default: fmt.Println("come into default") time.Sleep(500 * time.Millisecond) } } }
輸出結果
. . . . got 0 got 0 got 0 got 0 got 0 after 3 second
簡直是車禍現場,幸虧設置了3s主動退出,那case的時候,有沒有辦法判斷這個channel是否關閉了呢,固然是能夠的,看下面的程序
func example() { tick := time.Tick(time.Second) after := time.After(3 * time.Second) channel := make(chan int, 1) go func() { channel <- 1 close(channel) }() for { select { case <-tick: fmt.Println("tick 1 second") case <-after: fmt.Println("after 3 second") return case value, ok := <- channel: if ok { fmt.Println("got", value) } else { fmt.Println("channel is closed") time.Sleep(time.Second) } default: fmt.Println("come into default") time.Sleep(500 * time.Millisecond) } } }
輸出結果
come into default got 1 channel is closed tick 1 second channel is closed channel is closed after 3 second
綜上能夠看出,經過 value, ok := <- channel
這種形式,ok獲取的就是用來判斷channel
是否關閉的,ok爲 true,表示channel正常,不然,channel就是關閉的