zoukankan      html  css  js  c++  java
  • 【转】关于 Goroutine Channel Select 的用法和理解

    原文:https://blog.csdn.net/jfkidear/article/details/88661693

    ------------------------------------------

    关于 Goroutine Channel Select 的用法和理解

    阅读 384

    收藏 13

    2017-05-18

    原文链接:blog.sodroid.com

    转载请注明本文地址,感谢 :)

    了解进程、线程、协程

    本文不详细解释这三个名词的意思,下面有一篇文章,不懂的同学可以参考看看。

    Goroutine的使用

    • Goroutine 奉行通过通信来共享内存,而不是共享内存来通信
    • 使用goroutine很简单,只需要用到一个关键字go,我们用一段代码来示例一下如何使用go关键字
    1.  
      package main
    2.  
      import (
    3.  
      "fmt"
    4.  
      )
    5.  
      func main() {
    6.  
      go Goroutine()
    7.  
      }
    8.  
      func Goroutine() {
    9.  
      fmt.Println("Goroutine")
    10.  
      }

    没有输出?

    • 执行了上面的代码后,你会发现,什么都没有输出,那么是什么问题呢?
    • 因为我们当前的程序,只是一个单线程的程序,main函数只要执行完毕后,就不会再管其他线程在做什么事情了,程序自动退出。 然后我们想到了一个办法,加一个sleep函数,让main函数等待Goroutine函数执行完毕后再退出。

    • 更改后的代码如下:

    1.  
      package main
    2.  
      import (
    3.  
      "fmt"
    4.  
      "time"
    5.  
      )
    6.  
      func main() {
    7.  
      go Goroutine()
    8.  
      time.Sleep(3 * time.Second)
    9.  
      }
    10.  
      func Goroutine() {
    11.  
      fmt.Println("Goroutine")
    12.  
      }
    • 不出所料,程序成功打印出了“Goroutine”字符串。

    引入Channel

    • 通过上面的实验,我们成功地用Goroutine,并打印出了一句话。现在问题来了,这个并不是我们想要的。为什么?如果我的Goroutine函数里的代码需要执行10s、20s甚至更多的时候,怎么办?难道还是继续Sleep更多的秒数吗?
    • 我们需要的是,当Goroutine函数执行完毕的时候,自动通知main函数。所以这里我们就引出了channel

    创建方式

    • channel使用make创建

      • chann := make(chan bool)
    • 往channel中传入数据

      • chann <- true
    • 输出channel中的数据

      • <- chann
    • 接着我们改进上面使用Sleep方式的代码,换成使用channel的版本

    • 代码如下:
    1.  
      package main
    2.  
      import (
    3.  
      "fmt"
    4.  
      )
    5.  
      func main() {
    6.  
       
    7.  
      chann := make(chan bool)
    8.  
      go func() {
    9.  
      fmt.Println("Goroutine")
    10.  
      chann <- true
    11.  
      }()
    12.  
      <- chann
    13.  
      }
    • 如果运行以上的代码片段后,你成功输出了Goroutine,那么说明我们的写法是没错的。现在我们来理解一下这段代码的意思,我们看看匿名函数外面的<-chann,这实际上是起到了阻塞的作用,当匿名函数里面的业务没有执行完并讲true传入channel中的时候,这个程序是不会退出的,匿名函数外面会一直在等待。当匿名函数里面执行到了chann <- true,也就是将true传入了channel的时候,就不会继续阻塞,这时候程序就会结束。这就是channel的作用。

    使用for range迭代输出Channel

    • 需要注意的是,当你在对channel 迭代的时候,必须在某个位置明确的关闭这个channel,不然程序就会死锁。我们用一个代码段来示例一下:
    1.  
      package main
    2.  
      import (
    3.  
      "fmt"
    4.  
      )
    5.  
      func main() {
    6.  
      chann := make(chan bool)
    7.  
      go func() {
    8.  
      fmt.Println("Goroutine")
    9.  
      chann <- true
    10.  
      close(chann) //关闭channel
    11.  
      }()
    12.  
      for v := range chann {
    13.  
      fmt.Println(v)
    14.  
      }
    15.  
      }

    Channel 的定义类型

    • Channel 类型的定义格式:
      • ChannelType = ("chan" | "chan" "<-" | "<-" "chan")
    • Channel包含了三种类型的定义,可选的<-代表的是Channel的方向。没有指定方向的话,Channel就是双向的,既可以接受数据,也可以发送数据。
      • chan T //可以接收也可以发送
      • chan <- bool //可以发送bool类型的数据
      • <- chan bool //可以接收int类型的数据

    无缓冲Channel与缓冲Channel

    无缓冲Channel

    • 定义方式 chann := make(chan int)
    • 所谓无缓冲Channel就是在通道中只能传入1个值,若是传入的值一直没有被取走,那么就会一直阻塞着,直到被取走。可以说是同步阻塞的
    • 需要注意的是,无缓冲Channel一定是先取后传。当代码中出现了取的操作时,发现Channel里面并没有值,那么就会发生阻塞,当Channel里有值了,才会停止阻塞。

    缓冲Channel

    • 定义方式 chann := make(chan int,100)
    • 有缓冲Channel,比如缓冲值是100,那么除非传入的值已经达到了100了,否则这个Channel中,还是可以不断地传值进去。当Channel满了,就会发生阻塞,等值被取走后,才可以继续传值。

    如何判断一组任务是否完成

    使用缓冲Channel

    1.  
      package main
    2.  
      import (
    3.  
      "fmt"
    4.  
      "runtime"
    5.  
      )
    6.  
      func main() {
    7.  
      runtime.GOMAXPROCS(runtime.NumCPU()) // 设置线程数为当前计算机的CPU数
    8.  
       
    9.  
      chann := make(chan bool, 10)
    10.  
      for i := 0; i < 10; i++ {
    11.  
      go Go(chann, i)
    12.  
      }
    13.  
      for i := 0; i < 10; i++ {
    14.  
      <-chann
    15.  
      }
    16.  
      }
    17.  
      func Go(chann chan bool, index int) {
    18.  
      a := 1
    19.  
      for i := 0; i < 100000000; i++ {
    20.  
      a += 1
    21.  
      }
    22.  
      fmt.Println(index, a)
    23.  
      chann <- true
    24.  
      }

    使用sync包

    • 通过设置一个任务组,大小为10。每完成一个任务就记录一次wg.Done(),当10个任务都完成的时候,程序自动退出。
    1.  
      package main
    2.  
      import (
    3.  
      "fmt"
    4.  
      "runtime"
    5.  
      "sync"
    6.  
      )
    7.  
      func main() {
    8.  
      runtime.GOMAXPROCS(runtime.NumCPU())
    9.  
      wg :=sync.WaitGroup{}
    10.  
      wg.Add(10)
    11.  
      for i := 0; i < 10; i++ {
    12.  
      go Go(&wg, i)
    13.  
      }
    14.  
      wg.Wait()
    15.  
      }
    16.  
      func Go(wg *sync.WaitGroup, index int) {
    17.  
      a := 1
    18.  
      for i := 0; i < 100000000; i++ {
    19.  
      a += 1
    20.  
      }
    21.  
      fmt.Println(index, a)
    22.  
      wg.Done()
    23.  
      }

    Select

    select 用于多个channel监听并收发消息,当任何一个case满足条件则会执行,若没有可执行的case,就会执行default,如果没有default,程序就会阻塞。

    1.  
      package main
    2.  
      import (
    3.  
      "fmt"
    4.  
      "time"
    5.  
      )
    6.  
      func main() {
    7.  
      chann := make(chan int)
    8.  
      go enqueue(chann)
    9.  
      for {
    10.  
      select {
    11.  
      case v, ok := <-chann:
    12.  
      if ok {
    13.  
      fmt.Println(v)
    14.  
      } else {
    15.  
      fmt.Println("close")
    16.  
      return
    17.  
      }
    18.  
      default:
    19.  
      fmt.Println("waiting")
    20.  
      }
    21.  
      }
    22.  
      }
    23.  
      func enqueue(chann chan int) {
    24.  
      time.Sleep(3 * time.Second)
    25.  
      chann <- 1
    26.  
      close(chann)
    27.  
      }

    参考

  • 相关阅读:
    腾讯2014 笔试
    iOS 并发编程之 Operation Queues
    iOS 架构
    loadView and viewDidLoad?
    Referring to weak self inside a nested block
    Weakify和strongify探究
    iOS开发的最佳实践
    iOS 书籍
    Object-C非正式协议与正式协议的区别
    解决element-ui中el-menu组件作为vue-router模式在刷新页面后default-active属性与当前路由页面不一致问题的方法
  • 原文地址:https://www.cnblogs.com/oxspirt/p/11253560.html
Copyright © 2011-2022 走看看