c := make(chan bool) //建立一個無緩衝的bool型Channel c <- x //向一個Channel發送一個值 <- c //從一個Channel中接收一個值 x = <- c //從Channel c接收一個值並將其存儲到x中 x, ok = <- c //從Channel接收一個值,若是channel關閉了或沒有數據,那麼ok將被置爲false ch chan int //可讀寫 ch1 chan<- int //ch1只能寫 ch2 <-chan int //ch2只能讀 channel是類型相關的,也就是一個channel只能傳遞一種類型
goroutine是Go語言中的輕量級線程實現,由Go運行時(runtime)管理.
先看一個例子:緩存
func Sub(i int) { fmt.Println("from sub func", i) } func main() { for i := 0; i < 5; i++ { Sub(i) } fmt.Println("from main") }
這個例子作了一件事情,在main函數中串行執行了5次Sub函數.安全
若是咱們須要Sub函數可以併發的執行,咱們加個go,將每個Sub函數放在goroutine中去(main函數其實也是個goroutine),代碼以下所示:併發
func Sub(i int) { fmt.Println("from sub func", i) } func main() { for i := 0; i < 5; i++ { go Sub(i) } fmt.Println("from main") }
編譯執行,你會發現只打印出了from main,Sub函數中字符並無打印出來.這是由於主函數main啓動了5個Sub函數後,並無等待它們完成即退出了!這顯然不是咱們要的結果,咱們使用go提供的消息通訊機制channel來重構代碼,保證Sub函數執行完成後,主函數再退出!函數
channel(信道)是什麼,簡單說,是goroutine之間互相通信的東西。相似咱們Unix上的管道(能夠在進程間傳遞消息), 用來goroutine之間發消息和接收消息。其實,就是在作goroutine之間的內存共享。spa
func Sub(ch chan int) { for i := 0; i < 5; i++ { fmt.Println("from sub func", i) } ch <- 1 // 向channel存消息,若是沒有其餘goroutine取走數據,那麼掛起Sub } func main() { ch := make(chan int) // 建立了一個無緩存的channel go Sub(ch) // 開啓一個goroutine來執行Sub fmt.Println("from main") <-ch // 從channel取消息,若是沒有寫入消息,掛起main }
無緩衝的信道在取消息和存消息的時候都會掛起當前的goroutine.線程
因此上面代碼的執行流程是:
建立ch信道
新開goroutine來執行Sub,但由於ch信道中寫入的消息沒有被取走,Sub掛起.
from main被打印出來.
<-ch 掛起了main,直到取到數據,這就保證了Sub完成後,main才結束.翻譯
因此無緩存信道,存入數據必須取走,只存不取或取空的數據,都將致使死鎖.blog
如下情形都是死鎖:隊列
//取不存在的消息: ch := make(chan int) <-ch //只寫不取: ch := make(chan int) ch <- 1 //多個channel,ch1等待ch2的消息,但ch2沒有消息寫入: ch1 := make(chan int) ch2 := make(chan int) ch1 <- <-ch2 <-ch1
無緩存信道只負責流通消息,任何對該信道的讀和寫,都阻塞信道.
帶緩存的channel,不只能夠流通數據,還能夠緩存數據,只有達到緩存最大數目後,也就是緩存滿了後,才阻塞信道,這聽起來很像隊列(Queue).其實,緩衝信道是先進先出的,咱們能夠把緩衝信道看做爲一個線程安全的隊列進程
ch := make(chan int, 2) // 帶緩存channel,緩存數目2個,放入2個數據,不會掛起,只有放入第3個的時候才掛起當前goroutine
示例代碼:
func main() { ch := make(chan int, 3) ch <- 1 ch <- 2 ch <- 3 fmt.Println(<-ch) fmt.Println(<-ch) fmt.Println(<-ch) }
對ch的寫入,由於沒有超過緩存數目3,因此不會阻塞;對ch取數據,將按先進先出,依次輸出1 2 3
你會發現,帶緩存的信道,取數據仍是挺麻煩的.咱們用for range來取數據
func main() { ch := make(chan int, 3) ch <- 1 ch <- 2 ch <- 4 for v := range ch { fmt.Println(v) } }
報出deadlock,緣由是ch沒有關閉的情形下,range一直在讀取.加一個判斷:
func main() { ch := make(chan int, 3) ch <- 1 ch <- 2 ch <- 4 for v := range ch { fmt.Println(v) if len(ch) <= 0 { break } } }
咱們判斷了ch中有沒有數據,沒有數據就跳出循環.能夠正常輸出.固然咱們也能夠顯式關閉信道.
func main() { ch := make(chan int, 3) ch <- 1 ch <- 2 ch <- 4 close(ch) // 關閉信道 for v := range ch { fmt.Println(v) } }
1.無緩存
const MAX = 1000 var ch chan int func Sub(i int) { fmt.Println(i) ch <- 1 } func main() { ch = make(chan int) for i := 0; i < MAX; i++ { go Sub(i) } for i := 0; i < MAX; i++ { <-ch } }
2.帶緩存
const MAX = 1000 var ch chan int func Sub(i int) { fmt.Println(i) ch <- 1 } func main() { ch = make(chan int, MAX) for i := 0; i < MAX; i++ { go Sub(i) } for i := 0; i < MAX; i++ { <-ch } }
二者效果相同,不一樣點在於:
無緩衝的信道是一批數據一個一個的流進流出
緩衝信道則是一個一個存儲,而後一塊兒流出去
顧名思義,單向channel只能用於發送或者接收數據。channel自己必然是同時支持讀寫的,不然根本無法用。假如一個channel真的只能讀,那麼確定只會是空的,由於你沒機會往裏面寫數據。同理,若是一個channel只容許寫,即便寫進去了,也沒有絲毫意義,由於沒有機會讀取裏面的數據。所謂的單向channel概念,其實只是對channel的一種使用限制。
ch1 chan<- int //ch1只能寫
ch2 <-chan int //ch2只能讀
示例: func Parse(ch <-chan int) { for value := range ch { fmt.Println("Parsing value", value) } }