微信号:Golangweb

介绍:欢迎来到 Go语言社区 社区网址:www.golangweb.com APP网址:app.golangweb.com 社区非公司性质,完全个人爱好建立;做的不好的地方大家见谅.

go routine & channel 学习小结

2017-11-28 22:30 Allen在学习

作者:Allen在学习
链接:http://www.jianshu.com/p/2491da93daa6
來源:简书

共 3459 字,阅读需 9 分钟

小结一下go中的channel

使用方法

  1. channel分为有缓冲和无缓冲。

  2. 默认 channel是可同时支持存取。 除非指定:
    -- receivechan chan <- 只能接受数据
    -- sendchan <-chan 只能发送数据

  3. make(chan string) 无缓冲,容量为1 。

  4. make(chan string, 2) 有缓存。

同步

利用无缓冲的channel来同步

如下代码起了一个协程,方法里对一个无缓冲的channel进行取值操作,初始时没有值,所以被block,等到赋值之后 协程才得以跑完,主程序得以结束:

func worker(done chan bool) {
  fmt.Print("working...")
  time.Sleep(time.Second)
  fmt.Println("done")
  done <- true
}

func main() {
  done := make(chan bool, 1)
  go worker(done)
  <-done
}

结果为:

working...done

多个channel

当有多个channel时可以通过select来控制,如下两种情况:

没有default

select {
    case u:= <- ch1:
    case v:= <- ch2:
}

若ch1和ch2都阻塞,那select会一直等到某个channel可以操作为止。若是两个都可以操作,select会随机选一个进行操作。

有default的情况

select {
    case u:= <- ch1:
    case v:= <- ch2:
default:
    fmt.Println("no value")
}

若都是阻塞状态,则选择执行default, 有如下经典案例:

func main() {
    messages := make(chan string)
    msg := "hi"
    select {
        case messages <- msg:
            fmt.Println("sent message", msg)
        default:
            fmt.Println("no message sent")
    }
    // 因为使用的是无缓冲的channel,无法在非协程内对其赋值,所以该赋值操作被block,所以走的default. 

    // 若是 messages := make(chan string, 1)  变成有缓冲的channel,则此处可以赋值且不被阻塞, 则输出:sent message hi   
}

超时操作

time.Ticker

用法: ticker := time.NewTicker(1000000000) 里面的值是纳秒 ,来控制多久往ticker.C里面发送一下当前时间。 如下例子:

func f1(c chan int, v int)  {
  time.Sleep(time.Second * 2)
  c <- v
}

func f2(c chan int, v int)  {
  time.Sleep(time.Second * 3)
  c <- v
}

func main()  {
  ch1 := make(chan int)
  ch2 := make(chan int)
  ticker := time.NewTicker(1000000000)  //每隔一秒想ticker里发送一下当前的时间
  for i :=0; i<5 ;  i++{
    go f1(ch1, i)
    go f2(ch2, i)
  }
  var count int32 = 0
  for {
    select {
    case v1 := <- ch1:
        fmt.Printf("ch1 value: %d  \n", v1)
    case v2 := <- ch2:
        fmt.Printf("ch2 value: %d  \n", v2)
    case <- ticker.C:
        count ++
        fmt.Printf("wait channel: %d  \n", count)
    }
    if count ==5 {
        break
    }
  }
}

输出结果如下:

wait channel: 1  
wait channel: 2  
ch1 value: 1  
ch1 value: 4  
ch1 value: 3  
ch1 value: 0  
ch1 value: 2  
wait channel: 3  
ch2 value: 2  
ch2 value: 4  
ch2 value: 1  
ch2 value: 3  
ch2 value: 0  
wait channel: 4  
wait channel: 5  
Process finished with exit code 0

分析:先起了10个协程,两个channel中最快的要两秒才可以被赋值,所以轮询的前两秒里的select都会走 ticker的case,然后 ch1就可以进行取值赋值操作,由于执行所需时长极短,所以一秒内就完成了。然后第三秒时,ticker.C先于ch2拿到值 先执行ticker的case,然后下个轮询开始进行ch2的存取,完成之后等待 ticker拿到下个值,直至程序结束

time.After()

和time.Ticker的区别是这个只赋值一次,案例如下:

func main() {
  c1 := make(chan string, 1)
  go func() {
    time.Sleep(time.Second * 2)
    c1 <- "result 1"
}()
select {
  case res := <-c1:
      fmt.Println(res)
  case <-time.After(time.Second * 1):
      fmt.Println("timeout 1")
}
c2 := make(chan string, 1)
go func() {
    time.Sleep(time.Second * 2)
    c2 <- "result 2"
}()
select {
  case res := <-c2:
      fmt.Println(res)
  case <-time.After(time.Second * 3):
      fmt.Println("timeout 2")
  }
}

结果:

timeout 1
result 2
Program exited.

 
Golang语言社区 更多文章 Gost -- 一个极简且有用的GOPATH管理工具 Golang随机time.sleep的Duration问题 Golang中巧用defer进行错误处理 golang profile用法 用生产者消费者理解golang channel
猜您喜欢 Spark Streaming的还原药水——Checkpoint 大数据技术深观察:从具体场景说开去 SikuliX+Mobizen 的自动化快餐 Java中的类与对象 代自己:《致和菜头书》