一、channel(通道)的介紹
如果說goroutine是GO併發的執行體,channel(通道)就是他們的連線。channel是可以讓一個goroutine傳送特定值到另一個goroutine的通訊機制。
每一個通道是一個具體的型別,叫做通道的元素型別。如有一個int型別的通道就寫成:chan int
當然這裡也可以使用內建函式——make函式來建立一個通道:
ch := make(chan int) //ch是 'chan int ' 型別
二、 channel的語法
- chanDemo()
建立channel:
和普通的int、float等型別是一樣
我們也可以使用內建函式make來建立:var ch1 chan int //此時 ch1 == nil var ch2 chan float32 //此時 ch2 == nil var ch3 chan string //此時 ch3 == nil
ch1 := make(chan int) ch2 := make(chan float32) ch3 := make(chan string)
package main import "fmt" func main(){ ch := make(chan int) //建立channel ch <- 1 //向channel裡發資料 n := <- ch //從channel收資料 fmt.Println(n) }
這裡會報錯:all goroutine air asleep - deadlock!
原因:channel是用於goroutine和goroutine之間的通訊管道,在上面的程式碼中我們只有一個主goroutine(main)所以沒有人來接收ch中的資訊,會造成死鎖。
這裡我們需要啟動一個goroutine:
package main
import "fmt"
func chanDemo() {
ch := make(chan int)
go func(){ //參見函數語言程式設計:https://learnku.com/articles/59902
for {
n := <-ch
fmt.Println(n)
}
}()
ch <- 100
ch <- 200
ch <- 300
time.Sleep(time.Millisecond) //為了讓所有資料輸出,需要規定程式執行時間
}
func mian(){
chanDemo()
}
列印結果為:
100
200
300
- channel可作為引數
在函數語言程式設計中函式是一等公民,函式可作為引數、返回值等
channel也一樣,也可以作為引數,返回值。
package main
import "fmt"
func worker(id int ch chan int ){ //將channel作為引數
for {
n := <- ch
fmt.Printf("worker %d received %d\n",id, n )
}
}
func chanDemo(){
ch := make( chan int)
go worker() //開一個併發
ch <- 100
ch <- 200
ch <- 300
time.Sleep(time.Millisecond) //為了讓所有資料輸出,需要規定程式執行時間
}
func main(){
chanDemo()
}
列印結果為:
worker 0 received 100
worker 0 received 200
worker 0 received 300
這裡我們可以隨意建立,建立10goroutine:
將chanDemo()改一下
func chanDemo(){
var channels [10]chan int //建立channel陣列
for i := 0; i < 10; i++{
channels[i] = make(chan int)
go worker(i, channels[i]) //建立10個goroutine
}
for i := 0; i < 10; i++{
channels[i] <- 'a' + 1
}
time.Sleep(time.Millisecond)
}
列印結果:
worker 4 received 98
worker 0 received 98
worker 1 received 98
worker 2 received 98
worker 9 received 98
worker 6 received 98
worker 3 received 98
worker 8 received 98
worker 5 received 98
worker 7 received 98
* 分析:在修改的chanDemo()中,我們開了10個goroutine,而每一個goroutine都分發了一個channel,從達到每一個goroutine都可以和主goroutine(main)通訊。*
- channel可作返回值
同樣channel也可以作為返回值
列印結果:func creatworker(id int) chan int { c := make(chan int) go func() { for { n := <-c fmt.Printf("worker %d received %d\n", id, n) } }() return c //在creatworker()中主要是go func()在真正的在做事,c建立後立即被返回 } // func chanDemo(){ var channels [10]chan int //建立channel陣列 for i := 0; i < 10; i++{ channels[i] = creatworker(i) } for i := 0; i < 10; i++{ channels[i] <- 'a' + 1 } time.Sleep(time.Millisecond) } // func main() { chanDemo() }
worker 9 received 98
worker 8 received 98
worker 5 received 98
worker 6 received 98
worker 0 received 98
worker 3 received 98
worker 2 received 98
worker 1 received 98
worker 4 received 98
worker 7 received 98
3.bufferedChannel(緩衝通道)
在前面內容裡:
package main
import "fmt"
func main(){
ch := make(chan int) //建立channel
ch <- 1 //向channel裡發資料
n := <- ch //從channel收資料
fmt.Println(n)
}
會報錯:all goroutine air asleep - deadlock!
是因為沒有人去收channel的資料,但是在上面程式碼中我們發了1,就必須收1,這樣比較好資源,所以我們使用緩衝通道,就可有避免死鎖了。
我們這樣定義:
ch := make(chan int, 3) //建立一個緩衝容量為3的通道
func bufferedChannel(){
ch := make(chan int, 3)
ch <- 1
ch <- 2
ch <- 3
}
func main() {
bufferedChannel()
}
這樣run就不會deadlock,當然如果再向ch發資料就會deadlock。
現在仍然使用goroutine來收資料:
func worker(id int,c chan int){
for {
n := <-c
fmt.Printf("worker %d received %c\n", id, n)
}
}
func bufferedChannel(){
ch := make(chan int, 3)
go worker(0, ch)
for i := 0; i < 10; i++{
ch <- 'a' + i
}
time.Sleep(time.Millisecond)
}
可以看出,只要有人收,緩衝區滿了,也不會deadlock。
列印結果:
worker 0 received a
worker 0 received b
worker 0 received c
worker 0 received d
worker 0 received e
worker 0 received f
worker 0 received g
worker 0 received h
worker 0 received i
- channelClose
channel什麼時候發完了?
在前面的程式碼中,我們知道channel發完了,原因是:我們在main中呼叫的函式執行結束了,main結束了,程式也就退出了,在併發程式設計中,我們需要知道資料是什麼時候傳送結束的。
(1). close()方法
func worker(id int,c chan int){ for { n := <-c fmt.Printf("worker %d received %c\n", id, n) } } func channelClose(){ ch := make(chan int, 3) go worker(0, ch) ch <- 'a' ch <- 'b' ch <- 'c' ch <- 'd' close(ch) time.Sleep(time.Millisecond) } func main(){ channelClose() }
使用Close方法後資料收完後,就一直列印(列印time.Millisecond的時間)空串(或0)
列印結果:
worker 0 received a
worker 0 received b
worker 0 received c
worker 0 received d
worker 0 received
worker 0 received
worker 0 received
worker 0 received
worker 0 received
worker 0 received
worker 0 received
worker 0 received
worker 0 received
……
……
……
(2). n, ok := <- c
func worker(id int,c chan int){ for { n, ok := <- c if !ok{ break } fmt.Printf("worker %d received %c\n", id, n) } } func channelClose(){ ch := make(chan int) go worker(0, ch) ch <- 'a' ch <- 'b' ch <- 'c' ch <- 'd' close(ch) time.Sleep(time.Millisecond) } func main() { channelClose() }
列印結果:
worker 0 received a
worker 0 received b
worker 0 received c
worker 0 received d
(3). range
func worker(id int,c chan int){ for n := range c{ fmt.Printf("worker %d received %c\n", id, n) } } func channelClose(){ ch := make(chan int) go worker(0, ch) ch <- 'a' ch <- 'b' ch <- 'c' ch <- 'd' close(ch) time.Sleep(time.Millisecond) } func main() { channelClose() }
列印結果:
worker 0 received a
worker 0 received b
worker 0 received c
worker 0 received d
本作品採用《CC 協議》,轉載必須註明作者和本文連結