基础
基本使用
- 带缓冲区channel:定义声明时候制定了缓冲区大小(长度),可以保存多个数据。用于通信
- channel :=make(chan int,3)
- 不带缓冲区channel:只能只能存一个数据,用于两个groutine的同步,阻塞式
- channel :=make(chan bool)
- 只读管道 read_only := make (<-chan int)
只写管道 rite_only := make (chan<- int)
func main() {//定义channel :=make(chan int,3)//存数据channel <- 1channel <- 2channel <- 3//channel <- 4,报错,容量已经满了//取数据,如果只想删掉该值,直接<- channelnum :=<- channelfmt.Println(num)//1channel <- 4num2,ok:=<- channelif ok{fmt.Println(num2)//2}}
注意事项
管道只能存放指定类型数据
- 非缓冲通道上如果发生了流入无流出,或者流出无流入,就会引起死锁。
- 当一个协程存一个协程取,虽然存的快而取得慢,导致容量占满,也不会发生错误,而是负责存的协程会堵塞在channel <- num
- 当一个协程存一个协程取,虽然存的慢而取得快,导致channel为空,也不会发生错误,而是负责取的协程会堵塞在 num := <- channel
- 存满了的channel就不能继续存了,当从存满了的channel取出数据后还可以继续存
当要取某一个值时,要将它前面的值剔除
func main() {//定义channel :=make(chan int,3)//存数据channel <- 1channel <- 2channel <- 3//取到2,得先将1剔除<- channelres,ok:=<-channelif ok{fmt.Println(res)}}
关闭与遍历
当channel关闭后,只能取不能存
- 方法:close(channel)
- 垃圾回收器会在 channel 不可达时回收它,即使 channel 还未关闭。
- 使用 for range 遍历,同go程中编译器发现channel未被关闭,则会引发deadlock错误
如果channel无数据可读,那么for range会处于等待状态,只有当channel关闭,for range循环才会退出
func main() {//定义channel :=make(chan int,10)//存数据channel <- 1channel <- 2channel <- 3close(channel)for k :=range channel{fmt.Println(k)}}
如果采用for循环已经被关闭的管道,当管道没有数据时,读取的数据是管道的默认值,并且循环不会退出。res,ok :=<- channel,其中ok为false
func main() {channel :=make(chan int,10)channel <- 1channel <- 2channel <- 3close(channel)for {res,ok :=<-channelif ok {fmt.Println(res)}else {time.Sleep(time.Second)fmt.Printf("channel无数据,取到的只为%v\n",res)}}}
goroutine&channel
死锁现场一:
func main() {ch := make(chan int)<- ch // 阻塞main goroutine, 通道被锁}解决:func main() {ch := make(chan int)go func() {ch<-1}()res :=<- chfmt.Println(res)}注意:groutine一定要在主线程对channel操作前开启,不然主线程阻塞在channel操作造成死锁,如果上面的管道是有缓冲的,则不会堵死
死锁现场二(无缓冲channel的陷阱):
func main() {cha, chb := make(chan int), make(chan int)go func() {cha <- 1 // cha通道的数据没有被其他goroutine读取走,堵塞当前goroutinechb <- 0}()<- chb // chb 等待数据的写}解决:func main() {cha, chb := make(chan int), make(chan int)go func() {cha <- 1 // cha通道的数据没有被其他goroutine读取走,堵塞当前goroutinechb <- 0}()<- cha //将cha也读出来。因为都是无缓冲的通道,同时也要注意,要严格按照存的顺序来取<- chb // chb 等待数据的写}
生产者与消费者(1个send,1个receive)
func producter(apples chan int){for i:=1;i<=50;i++{fmt.Println("生产者:我生产了第",i,"个苹果")apples <- i}close(apples)}func consumer(apples chan int,flag chan bool){for {res ,ok := <- applesif ok{fmt.Println("消费者:我吃了第",res,"个苹果")}else {break}}flag <- trueclose(flag)}func main() {apples := make(chan int,20)flag := make(chan bool)go producter(apples)go consumer(apples,flag)for{_ ,ok := <- flagif ok{break}}}
多协程求素数(1个send,n个receive)
func PutNum(channel chan int){for i:=1;i<=100000;i++{channel <- i}close(channel)}func PrimeNum(intchan chan int,primechan chan int ,exitchan chan bool){for{res,ok :=<- intchanif ok{if res>2{flag:=truefor i:=2;i<res;i++{if res % i ==0{flag=falsebreak}}if flag{primechan <-res}}}else {break}}exitchan <-truefmt.Println("一个线程已执行完毕")}func main() {intchan :=make(chan int,1000)//数据源管道primechan :=make(chan int,1000)//结果管道exitchan :=make(chan bool,6)//退出管道go PutNum(intchan)for i:=0;i<6;i++{go PrimeNum(intchan,primechan,exitchan)}go func() {for i:=0;i<6;i++{<- exitchan}close(primechan)}()for{res,ok:=<-primechanif !ok{break}fmt.Println(res)}}
多协程求素数(n个send,n个receive)
func PutNum(channel chan int ,exitintchan chan<- bool,i int){//exitintchan为只写管道rand.Seed(time.Now().UnixNano())for {value := rand.Intn(1000)if value ==i{exitintchan <-truereturn}else {channel<-value}}}func PrimeNum(intchan chan int,primechan chan int ,exitchan chan<- bool){for{res,ok :=<- intchanif ok{if res>2{flag:=truefor i:=2;i<res;i++{if res % i ==0{flag=falsebreak}}if flag{primechan <-res}}}else {break}}exitchan <-truefmt.Println("一个线程已执行完毕")}func main() {intchan :=make(chan int,1000)//数据源管道exitintchan :=make(chan bool,4) //数据源退出管道primechan :=make(chan int,1000)//结果管道exitchan :=make(chan bool,6)//结果退出管道for i:=0;i<4;i++{go PutNum(intchan,exitintchan,i)}go func() {for i:=0;i<4;i++{<- exitintchan}close(intchan)}()for i:=0;i<6;i++{go PrimeNum(intchan,primechan,exitchan)}go func() {for i:=0;i<6;i++{<- exitchan}close(primechan)}()for{res,ok:=<-primechanif !ok{break}fmt.Println(res)}}
future模式
将客户端请求的处理过程从同步改为异步,以便将客户端解放出来,在服务端程序处理期间可以去干点其他事情,最后再来取请求的结果。好处在于整个调用过程中不需要等待,可以充分利用所有的时间片段,提高系统的响应速度。
type query struct {request chan stringresponse chan string}func exec(q query) {go func() {sql :=<- q.request//用睡眠替代执行耗时time.Sleep(time.Second)q.response <- sql +"---我是结果"}()}func main() {q :=query{make(chan string,1),make(chan string,1)}go exec(q)q.request <- "我是请求"//用睡眠替代其他任务time.Sleep(time.Second)fmt.Println(<-q.response)}
