golang中的channel
channel用於goroutine之間的通信
如果不用channel,使用共享全局變量的方式,需要加鎖
// synchornized 同步
// golang中的 sync包中有互斥鎖
var lock sync.Mutex // mutex 互斥
lock.Lock() // 上鎖
// 多個goroutine同時對相同的數據進行修改
lock.Unlock() // 解鎖
使用同步鎖並發效率會很低
channel主要用於goroutine通信和解決主線程等待goroutine執行結束再退出的問題
basic concept of channel
- 本質上是一個FIFO的數據結構-隊列
- 線程安全,不需要加鎖
- channel是有類型的,如字符串channel
chan string
,只能保存string數據
declare channel
var variableName chan dataType
var c1 chan int
var c2 chan bool
var c3 chan map[int]string
channel 是引用類型,必須初始化后才能使用
var c1 chan int
c1 = make(chan int, 4) // 使用make函數初始化chan int, 4是容量
// 初識化之后容量不可動態變化,channel不會自動擴容,寫入數據時不能超過容量,否則會deadlock
// fatal error: all goroutines are asleep - deadlock!
c1 <- 666 // 寫入數據
num := <- c1 // 取出數據, 每取一個數據,channel長度減一
// 不使用goroutine的情況下
// 當數據全部取出是再對channel取數據會報 deadlock
使用channel的注意事項
- 只保存指定類型數據
- 數據滿后,不能在添加數據,否則deadlock
- 從channel中取出數據,長度減一,騰出一個位置可添加數據
- 不使用goroutine的情況下,當數據全部取出是再對channel取數據會報 deadlock
channel的遍歷和關閉
內置函數close(channel) 可以關閉channel,不能再添加數據,但是可以讀取數據
channel可以用for range方式遍歷
但是必須是已關閉的channel
for range遍歷一個未關閉的channel會出現deadlock
c1 := make(chan int, 10)
for i:=0; i<10; i++ {
c1 <- i
}
close(c1)
for v := range c1 {
fmt.Println(v)
}
對於一個已關閉的channel讀取數據時,如果數據已全部取出,取值狀態返回false而不會報錯
func main() {
c1 := make(chan int, 2)
c1 := make(chan int, 2)
c1 <- 1
c1 <- 2
c2 <- 1
c2 <- 2
close(c1)
<- c1 // 取出值 丟棄不用 1
<- c1 // 2
v, ok := <-c1 // 數據已取完
fmt.Println(v,ok) // 0 false
<- c2 // 1
<- c2 // 2
v, ok := <-c2 // 數據已取完 程序到這里會出錯 deadlock
fmt.Println(v,ok)
}
對於已關閉的管道,可以在取完數據時結束等待goroutine執行
for {
_, ok := <- exitChan bool
if !ok {
break
}
}
未關閉的channel需要取出指定個數的值之后結束等待goroutine執行
channel使用細節
channel默認是既可以讀又可以寫的
可以聲明為只讀或只寫
var c1 <- chan int // 只讀
var c2 chan <- int // 只寫
一般channel不會聲明為只讀和只寫,而是在聲明函數形參的時候使用
var c1 chan string
func sendData(c chan <- string) {...}
func readData(c <- chan string) {...}
// c1是默認可讀可寫的channel
// 當c1作為參數傳給 sendData時 在 sendData 函數中只允許對管道進行寫操作
// 當c1作為參數傳給 readData時 在 readData 函數中只允許對管道進行讀操作
goroutine中使用recover函數解決協程中出現的panic ,不影響主線程的執行
defer func() {
if err = recover(); err != nil {
...dosomething
}
}
select
select用於解決從管道中取數據的阻塞問題
不使用select,在遍歷未關閉的管道時會deadlock
然而,很多情況下管道都是未關閉的,因為不好確定什么時候關
用select來遍歷未關閉管道,不會deaklock
func main() {
c1 := make(chan int, 10)
c2 := make(chan string, 5)
for i := 0; i < 10; i++ {
rand.Seed(time.Now().UnixNano())
c1 <- rand.Intn(100)
}
c2 <- "commerce"
c2 <- "corresponding"
c2 <- "oblige"
c2 <- "decline"
c2 <- "praise"
label1:
for {
select {
case v := <-c1:
fmt.Println(v)
case v := <-c2:
fmt.Println(v)
default:
fmt.Println("兩個管道都沒數據了吧")
break label1
}
}
fmt.Println("adhere")
}