channel簡介
channel俗稱管道埋合,用于數(shù)據(jù)傳遞或數(shù)據(jù)共享备徐,其本質(zhì)是一個先進先出的隊列,使用goroutine+channel進行數(shù)據(jù)通訊簡單高效甚颂,同時也線程安全蜜猾,多個goroutine可同時修改一個channel,不需要加鎖 振诬。
channel可分為三種類型:
只讀channel:只能讀channel里面數(shù)據(jù)蹭睡,不可寫入
只寫channel:只能寫數(shù)據(jù),不可讀
一般channel:可讀可寫
channel使用
定義和聲明
var readOnlyChan <-chan int // 只讀chan
var writeOnlyChan chan<- int // 只寫chan
var mychan chan int //讀寫channel
//定義完成以后需要make來分配內(nèi)存空間赶么,不然使用會deadlock
mychannel = make(chan int,10)
//或者
read_only := make (<-chan int,10)//定義只讀的channel
write_only := make (chan<- int,10)//定義只寫的channel
read_write := make (chan int,10)//可同時讀寫
//操作
write_only <- "wd" //寫數(shù)據(jù)
a := <- read_only //讀取數(shù)據(jù)
a, ok := <- read_only //優(yōu)雅的讀取數(shù)據(jù)
注:
- 讀寫操作注意:
- 管道如果未關(guān)閉肩豁,在讀取超時會則會引發(fā)deadlock異常
- 管道如果關(guān)閉進行寫入數(shù)據(jù)會pannic
- 當管道中沒有數(shù)據(jù)時候再行讀取或讀取到默認值,如int類型默認值是0
- 循環(huán)管道注意:
- 使用range循環(huán)管道辫呻,如果管道未關(guān)閉會引發(fā)deadlock錯誤清钥。
- 如果采用for死循環(huán)已經(jīng)關(guān)閉的管道,當管道沒有數(shù)據(jù)時候放闺,讀取的數(shù)據(jù)會是管道的默認值祟昭,并且循環(huán)不會退出。
示例代碼:
package main
import (
"fmt"
"time"
)
func main() {
mychannel := make(chan int,10)
for i := 0;i < 10;i++{
mychannel <- i
}
close(mychannel) //關(guān)閉管道
fmt.Println("data lenght: ",len(mychannel))
for v := range mychannel { //循環(huán)管道
fmt.Println(v)
}
fmt.Printf("data lenght: %d",len(mychannel))
}
帶緩沖區(qū)channel和不帶緩沖區(qū)channel
帶緩沖區(qū)channel:定義聲明時候制定了緩沖區(qū)大小(長度)雄人,可以保存多個數(shù)據(jù)从橘。
ch := make(chan int ,10) //帶緩沖區(qū)
不帶緩沖區(qū)channel:只能存一個數(shù)據(jù),并且只有當該數(shù)據(jù)被取出時候才能存下一個數(shù)據(jù)础钠。
ch := make(chan int) //不帶緩沖區(qū)
channel實現(xiàn)作業(yè)池
創(chuàng)建三個channel,一個channel用于接受任務叉谜,一個channel用于保持結(jié)果旗吁,還有個channel用于決定程序退出的時候。
package main
import (
"fmt"
)
func Task(taskch, resch chan int, exitch chan bool) {
defer func() { //異常處理
err := recover()
if err != nil {
fmt.Println("do task error:", err)
return
}
}()
for t := range taskch { // 處理任務
fmt.Println("do task :", t)
resch <- t //
}
exitch <- true //處理完發(fā)送退出信號
}
func main() {
taskch := make(chan int, 20) //任務管道
resch := make(chan int, 20) //結(jié)果管道
exitch := make(chan bool, 5) //退出管道
go func() {
for i := 0; i < 10; i++ {
taskch <- i
}
close(taskch)
}()
for i := 0; i < 5; i++ { //啟動5個goroutine做任務
go Task(taskch, resch, exitch)
}
go func() { //等5個goroutine結(jié)束
for i := 0; i < 5; i++ {
<-exitch
}
close(resch) //任務處理完成關(guān)閉結(jié)果管道停局,不然range報錯
close(exitch) //關(guān)閉退出管道
}()
for res := range resch{ //打印結(jié)果
fmt.Println("task res:",res)
}
}
select-case實現(xiàn)非阻塞channel
原理通過select+case加入一組管道很钓,當滿足(這里說的滿足意思是有數(shù)據(jù)可讀或者可寫)select中的某個case時候,那么該case返回董栽,若都不滿足case码倦,則走default分支。
package main
import (
"fmt"
)
func send(c chan int) {
for i :=1 ; i<10 ;i++ {
c <-i
fmt.Println("send data : ",i)
}
}
func main() {
resch := make(chan int,20)
strch := make(chan string,10)
go send(resch)
strch <- "wd"
select {
case a := <-resch:
fmt.Println("get data : ", a)
case b := <-strch:
fmt.Println("get data : ", b)
default:
fmt.Println("no channel actvie")
}
}
channel頻率控制
在對channel進行讀寫的時锭碳,go還提供了非常人性化的操作袁稽,那就是對讀寫的頻率控制,通過time.Ticke實現(xiàn)
package main
import (
"time"
"fmt"
)
func main(){
requests:= make(chan int ,5)
for i:=1;i<5;i++{
requests<-i
}
close(requests)
limiter := time.Tick(time.Second*1)
for req:=range requests{
<-limiter
fmt.Println("requets",req,time.Now()) //執(zhí)行到這里擒抛,需要隔1秒才繼續(xù)往下執(zhí)行推汽,time.Tick(timer)上面已定義
}
}