什麼是channel
go channel 是go語言一種核心資料型別,可以理解是一種管道,併發核心單元可以透過channel傳送或接收資料進行通訊,實現併發同步。
channel 型別
channel型別是可以有方向的,假設T是一種型別:
- chan T是雙向channel型別,編譯器允許對雙向channel同時進行傳送和接收。
- chan<- T是隻寫channel型別,編譯器只允許往channel裡面傳送資料。
- <-chan T是隻讀channel型別,編輯器只允許從channel裡面接收資料。
在go語言中,透過chan建立通道,建立語法:
channel_name 表示宣告的管道名,Type表示管道傳輸的資料型別。管道支援資料型別包括:int,string,struct等。channel1 := make(chan int) //雙向channel,可讀寫 channel2 := make(chan<- int) //單向只寫channel channel3 := make(<-chan int) //單向只讀channel
channel 分類
channel分無緩衝型別和帶緩衝型別:
- 無緩衝型別channel
一個執行緒向這個channel傳送了訊息後,會阻塞當前的這個執行緒,直到其他執行緒去接收這個channel的訊息。建立語法:intchan := make(chan int)
- 緩衝型別channel
帶緩衝的channel,是可以指定緩衝的訊息數量,當訊息數量小於指定值時,不會出現阻塞,超過之後才會阻塞,需要等待其他執行緒去接收channel處理。建立語法:intchan := make(chan int,3)
channel的一些操作
假設ch是一個通道。
- close(關閉一個通道)
注意:如果ch是一個nil通道,或者ch已經被關閉,上述close(ch)會觸發panicclose(ch)
- 向通道ch傳送一個值
注意:ch只能是雙向通道或者是單向只寫通道。ch<-c
- 從通道ch接收一個值
注意:通道ch只能是雙向通道或者是單向只讀通道c <-ch
- 獲取通道容量
cap(ch)
- 獲取通道長度
注意:len函式返回是當前通道元素個數,cap函式返回的是當前通道總共能存放元素個數。len(ch)
一些channel的使用例子
- 非緩衝通道實現資料同步
package main
import (
"fmt"
"time"
)
func main() {
c := make(chan int)
done := make(chan string)
go func(ch chan<- int, x int) {
time.Sleep(time.Second * 5)
ch <- x * x * x
}(c, 10)
go func(ch <-chan int) {
data := <-ch
fmt.Println(data)
s := "程式結束"
done <- s
}(c)
fmt.Println("程式開始")
fmt.Println(<-done)
}
結果輸出:
程式開始
1000
程式結束
- 非緩衝通道實現超時控制
package main
import (
"fmt"
"time"
)
func main() {
select {
case <-dowork():
fmt.Println("任務在規定時間內完成!")
case <-time.After(time.Second * 2):
fmt.Println("任務超時了!!!")
}
}
func dowork() <-chan int {
ch := make(chan int)
go func() {
fmt.Println("開始工作")
time.Sleep(time.Second * 3)
ch <- 0
}()
return ch
}
結果輸出:
開始工作
任務超時了!!!
- 帶緩衝通道實現生產者-消費者模型
package main
import (
"fmt"
"time"
)
func producer(ch chan int) {
for i := 0; i < 10; i++ {
ch <- i
}
close(ch)
}
func consumer(ch chan int, done chan string) {
for i := 0; i < 5; i++ {
go func(id int) {
for {
data, ok := <-ch
if !ok {
done <- "done"
return
} else {
fmt.Printf("消費者 %v,完成任務 %v\n", id, data)
time.Sleep(time.Second * 2)
}
}
}(i)
}
}
func main() {
done := make(chan string)
ch := make(chan int, 10)
go producer(ch)
go consumer(ch, done)
<-done
fmt.Println("任務完成")
}
結果輸出:
消費者 4,完成任務 0
消費者 1,完成任務 1
消費者 2,完成任務 2
消費者 3,完成任務 3
消費者 0,完成任務 4
消費者 0,完成任務 5
消費者 3,完成任務 8
消費者 2,完成任務 6
消費者 1,完成任務 9
消費者 4,完成任務 7
任務完成
- 帶緩衝通道實現併發數量控制
package main
import (
"fmt"
"time"
)
func handleEvent(done chan string, task chan bool) {
for i := 0; i < 10; i++ {
task <- true
go func(id int) {
fmt.Printf("處理事件 %v\n", id)
time.Sleep(time.Second * 1)
<-task
if id == 9 {
done <- "done"
}
}(i)
}
}
func main() {
done := make(chan string)
task := make(chan bool, 2) //並法數控制為2
go handleEvent(done, task)
<-done
fmt.Println("任務完成")
}
結果輸出:
處理事件 1
處理事件 0
處理事件 2
處理事件 3
處理事件 5
處理事件 4
處理事件 7
處理事件 6
處理事件 8
處理事件 9
任務完成
channel使用注意事項
- 關閉一個nil通道或者一個已經關閉的通道將產生一個panic。
- 向一個已關閉的通道傳送資料也將導致panic。
- 向一個nil通道傳送資料或者從一個nil通道接收資料將使當前協程永久阻塞。
本作品採用《CC 協議》,轉載必須註明作者和本文連結