golang channel的使用技巧

go的横空出世,让很多人眼前一亮,它的语法以简洁著称,并且它对多核并发的原生支持,让他在云计算和分布式领域展露头脚,它的核心围绕channel和goroutine展开。

首先golang channel 分为有缓冲与无缓冲两种类型,很多人认为无缓冲channel单单只是 默认缓冲为1缓冲的channel,其实它们最大的区别是阻塞问题。如下

c1:=make(chan int)        无缓冲  

c2:=make(chan int,1)      有缓冲

c1<-1                            

在c1<- 1处,因为c1是无缓冲的,因此程序会阻塞在这里,不会往下执行,但是c2则不会,他在channel满之前是不会阻塞的,他会继续往下执行,但是如果你在c2第一个数据未取出之前继续塞第二个数据,就会阻塞在你放第二个数据处,因为她的缓冲区单位只有1.例:

dgucochann := make(chan int,2)   

dgucochann <- 1

fmt.Println("Hello DGuco")

dgucochann <- 2    

fmt.Println("Hello Yuki")  

dgucochann <- 3  

fmt.Println("Hello Go")

在这种情况下,程序在第6行处会阻塞,会打印前两句话Hello DGuco和Hello Yuki,因为该channel只有两个单位的缓冲,在第6行时没有另外一个goroutine去取出里面的数据,所以阻塞在这里。

下面我们来介绍一下channel的基本用法。

1 信号量的传递

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
           
           
package main

import (
"fmt"
"time"
)

var mychan = make ( chan int )

func dgucofunc1 () {
fmt . Println ( "In dguco_func1" )
mychan <- 1
}

func dgucofunc2 () {
a := <- mychan
fmt . Println ( "In dguco_func1" )
}
func main () {
go dgucofunc1 ()
go dgucofunc2 ()
time . Sleep ( time . Millisecond * 10 )
}
 来自CODE的代码片
dgucosignal.go


可以看到依次打出了相应的两段文字,如果把第6行去掉,因为在dgucofunc2收不到来自channel的信号就会阻塞,结果只会打印In dguco_func1
2 生产者消费者问题
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
            
            
package main

import (
"fmt"
"time"
)

func dgucoproducer ( c chan int , max int ) {
for i := 0 ; i < max ; i ++ {
c <- i
}
}

func dgucoconsumer ( c chan int ) {
ok := true
for ok {
if value , ok := <- c ; ok {
fmt . Println ( value )
}
}
}

func main () {
c := make ( chan int )
defer close ( c )
go dgucoproducer ( c , 10 )
go dgucoconsumer ( c )
time . Sleep ( time . Millisecond * 10 )
}
 来自CODE的代码片
dgucocustomer.go
这个很简单,执行结果依次打印0-9
3  定时器(这个经常用)
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
             
             
package main

import "time"
import "fmt"

var mychannel = make ( chan bool )

func dgucotimertask () {
fmt . Println ( "My TimerTask.." )
}

func dgucotimer () {
//返回一个定时器
mytimer := time . NewTicker ( time . Millisecond * 1000 )
select {
case <- mytimer . C :
go dgucotimertask ()
}
}

func main () {
dgucotimer ()
time . Sleep ( time . Millisecond * 10 )
}
 来自CODE的代码片
dgucotimer.go
顺便说一下select的作用,首先它就相当于switch,但是他是用于channel的,举个例子:

select{
case <- c1:
.........
case <- c2:
........:
当程序执行到这里时,程序会阻塞在此,直到c1或者c2中有数据被放入就会继续执行,当然一般不会这样写,因为如果c1,和c2永远收不到数据程序阻塞在这里肯定不是我们想要的结果,一般我们都会写一个超时处理。
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
            
            
package main

import "time"
import "fmt"

var mychannel = make ( chan bool )

func dgucotimertask () {
fmt . Println ( "My TimerTask.." )
}

func dgucotimer () {
timeout := time . NewTicker ( time . Millisecond * 10000 )

select {
case <- mychannel :
go dgucotimertask ()
case <- timeout . C :
fmt . Println ( "Time out" )
}
fmt . Println ( "Hello go" )
}

func main () {
dgucotimer ()
time . Sleep ( time . Millisecond * 10 )
}
 来自CODE的代码片
dgucotimeout.go
这里我们是没有向mychannel中写数据的,但是等到10秒之后会打印出hello go,程序并没有因此阻塞在这里。

4 goroutine 通信
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
            
            
package main

import "time"
import "fmt"

var mychannel = make ( chan int )

func dgucoroutine () {
for i := 0 ; i < 10 ; i ++ {
mychannel <- i
}

}
func dgucoroutine1 () {
for {
i := <- mychannel
fmt . Println ( "dgucoroutine1:" , i )
}
}

func dgucoroutine2 () {
for {
i := <- mychannel
fmt . Println ( "dgucoroutine2:" , i )
}
}

func main () {
go dgucoroutine ()
go dgucoroutine1 ()
go dgucoroutine2 ()
// mychannel <- 4
time . Sleep ( time . Millisecond * 100 )
}
 来自CODE的代码片
dgucodemo.go

这里实现了一个goroutine写,两个goroutine读的过程,可以看到按顺序写进去的数据,被哪个goroutine读到是完全随机的,在golang中我们要实现进程间通信,channel是唯一途径,也是推荐的途径,她的底层是通过共享内存实现的,速度非常快,这样我们摆脱了linux系统繁琐,古老的进程间通信方式,管道,消息队列,信号量等,非常的方便。
最后做一个总结,在使用channel的时候,一定要至少有一个goroutine来负责读(除非你有特殊的需求),否则你的程序就会阻塞在你写channel的地方。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值