√ golang社區口號:不要經過共享內存來通訊,而應該經過通訊來共享內存。golang
√ golang提供一種基於消息機制而非共享內存的通訊模型。消息機制認爲每一個併發單元都是自包含的獨立個體,而且擁有本身的變量,但在不一樣併發單元間這些變量不共享。每一個併發單元的輸入和輸出只有一種,那就是消息。安全
√ channel是golang在語言級提供的goroutine間的通訊方式,可使用channel在兩個或多個goroutine之間傳遞消息。 併發
√ channel是進程內的通訊方式,若是須要跨進程通訊,建議使用分佈式的方法來解決,好比使用Socket或HTTP等通訊協議。 異步
√ channel是類型相關的,即一個channel只能傳遞一種類型的值,須要在聲明channel時指定。能夠認爲channel是一種類型安全的管道。分佈式
var chanName chan ElementType
var ch chan int // int類型channel var m map[string]chan bool // bool類型channel的map
√ 定義一個channel直接使用內置的函數make()便可。函數
// 聲明一個channel var chanName chan ElementType // 定義一個無緩衝的channel chanName := make(chan ElementType)
// 定義一個帶緩衝的channel
chanName := make(chan ElementType, n)
√ 關閉一個channel直接使用內置的函數close()便可。spa
√ 應該在生產者處關閉channel,而不是消費者處關閉channel,不然容易引發panic。code
// 聲明一個channel var chanName chan ElementType // 定義一個無緩衝的channel chanName := make(chan ElementType) // 定義一個帶緩衝的channel chanName := make(chan ElementType, n)
// 關閉一個channel
close(chanName)
√ 向無緩衝的channel寫入數據會致使該goroutine阻塞,直到其餘goroutine從這個channel中讀取數據。blog
√ 向帶緩衝的且緩衝已滿的channel寫入數據會致使該goroutine阻塞,直到其餘goroutine從這個channel中讀取數據。進程
√ 向帶緩衝的且緩衝未滿的channel寫入數據不會致使該goroutine阻塞。
√ 從無緩衝的channel讀出數據,若是channel中無數據,會致使該goroutine阻塞,直到其餘goroutine向這個channel中寫入數據。
√ 從帶緩衝的channel讀出數據,若是channel中無數據,會致使該goroutine阻塞,直到其餘goroutine向這個channel中寫入數據。
√ 從帶緩衝的channel讀出數據,若是channel中有數據,該goroutine不會阻塞。
√ 總結:無緩衝的channel讀寫一般都會發生阻塞,帶緩衝的channel在channel滿時寫數據阻塞,在channel空時讀數據阻塞。
// 聲明一個channel var chanName chan ElementType // 定義一個無緩衝的channel chanName := make(chan ElementType)
// 定義一個帶緩衝的channel
chanName := make(chan ElementType, n) // 寫channel chanName <- value // 讀channel value := <-chanName
√ golang中的range經常和channel一塊兒使用,用來從channel中讀取全部值。
√ range操做可以不斷讀取channel裏面的數據,直到該channel被顯式的關閉。
for value := range chanName { // ... }
package main import "fmt" func generateString(strings chan string) { strings <- "Monday" strings <- "Tuesday" strings <- "Wednesday" strings <- "Thursday" strings <- "Friday" strings <- "Saturday" strings <- "Sunday" close(strings) } func main() { strings := make(chan string) // 無緩衝channel go generateString(strings) for s := range strings { fmt.Println(s) } }
√ golang中的select關鍵字用於處理異步IO,能夠與channel配合使用。
√ golang中的select的用法與switch語言很是相似,不一樣的是select每一個case語句裏必須是一個IO操做。
√ select會一直等待等到某個case語句完成才結束。
select { case <-chan1: // 若是chan1成功讀到數據,則進行該case處理語句 case chan2 <- 1: // 若是成功向chan2寫入數據,則進行該case處理語句 default: // 若是上面都沒有成功,則進入default處理流程 }
package main import "fmt" import "time" func main() { timeout := make(chan bool) go func() { time.Sleep(3 * time.Second) // sleep 3 seconds timeout <- true }() // 實現了對ch讀取操做的超時設置。 ch := make(chan int) select { case <-ch: case <-timeout: fmt.Println("timeout!") } }
√ 在讀取的時候使用多重返回值來判斷一個channel是否已經被關閉。
value, ok := <-chanName if ok { // channel未關閉 } else { // channel已關閉 }
package main import "fmt" func generateString(strings chan string) { strings <- "Monday" strings <- "Tuesday" strings <- "Wednesday" strings <- "Thursday" strings <- "Friday" strings <- "Saturday" strings <- "Sunday" close(strings) } func main() { strings := make(chan string) // 無緩衝channel go generateString(strings) for { if s, ok := <-strings; ok { fmt.Println(s) } else { fmt.Println("channel colsed.") break } } }
√ golang中假如一個channel只容許讀,那麼channel確定只會是空的,由於沒機會往裏面寫數據。
√ golang中假如一個channel只容許寫,那麼channel最後只會是滿的,由於沒機會從裏面讀數據。
√ 單向channel概念,其實只是對channel的一種使用限制,即在將一個channel變量傳遞到一個函數時,能夠經過將其指定爲單向channel變量,從而限制該函數中能夠對此channel的操做,達到權限控制做用。
var ch1 chan elementType // ch1是一個正常的channel var ch2 chan<- elementType // ch2是單向channel,只用於寫數據 var ch3 <-chan elementType // ch3是單向channel,只用於讀數據
ch1 := make(chan elementType) ch2 := <-chan elementType(ch1) // ch2是一個單向的讀取channel ch3 := chan<- elementType(ch1) // ch3是一個單向的寫入channel
package main import "fmt" func Parse(ch <-chan int) { for value := range ch { fmt.Println("Parsing value", value) } } func main() { var ch chan int ch = make(chan int) go func() { ch <- 1 ch <- 2 ch <- 3 close(ch) }() Parse(ch) }
咱們已經知道golang程序從main()函數開始執行,當main()函數返回時,程序結束且不等待其餘goroutine結束。若是main函數使用time.Sleep方式阻塞等待全部goroutine返回,那麼這個休眠時間勢必沒法控制精確。經過使用channel能夠很好解決這個問題。
package main import "fmt" func MyRoutineFunc(ch chan int) { // 函數處理 ch <- 1 fmt.Println("MyRoutineFunc process finished.") } func main() { chs := make([]chan int, 10) for i := 0; i < 10; i++ { chs[i] = make(chan int) go MyRoutineFunc(chs[i]) } for _, ch := range chs { <-ch } fmt.Println("All goroutine finished.") }
golang沒有提供直接的超時處理機制,但咱們能夠利用select和channel結合來實現超時機制。
package main import "fmt" import "time" func main() { // 實現並執行一個匿名的超時等待函數 timeout := make(chan bool, 1) go func() { time.Sleep(3 * time.Second) // 等待3秒鐘 timeout <- true }() // 而後結合使用select實現超時機制 ch := make(chan int) select { case <-ch: // 從ch中讀取到數據 case <-timeout: // 一直沒有從ch中讀取到數據,但從timeout中讀取到了數據 fmt.Println("timeout!") } }