搞定 channel, 看完这一篇,足够!!!

81 阅读2分钟

并发编程通信方式之一-channel

  1. 概念-特性:go 语言channel 是引用类型,goroutine 间通过channel通信,遵循先入先出规则,保证收发数据顺序

  2. 创建方式:使用make 句柄创建,make(chan 数据类型)

  3. 向管道发送数据,格式如下:

     test := make(chan interface{})  //此出不谈容量问题
     test <- "hello"
     test <- 0
    
     * 发送需要了解的理论知识点:向管道发送数据,如无接受方,发送操作持续阻塞,用户态协成完全由实现方控制,所以需要实现方要写对等代码,有发送方,一定需要有接收方,即使我们的管道是含容量,容量堆满,也终究阻塞,所以有无容量,理论并不矛盾
    
  4. 收发不对等报错:fatal error: all goroutines are asleep - deadlock!

  5. 通过管道接受数据,所涉及知识点如下:

     - 非阻塞接收(使用该方式接收数据,语句不发生阻塞):
     - result, ok := <-ch   result 接受到的数据,没有数据为0值,引用类型,没有值默认0值, ok 是否接受到数据,bool类型,ch 管道变量
    
     - 阻塞接收(使用该方式接收数据,语句会发生阻塞):
     - result := <-ch
    
     - 丢弃接受数据,一般为了实现收发对等
     - <-ch
    
  6. 管道数据遍历,格式如下:

     for data := range ch {}        
    
     通道不关闭,range 函数就不会结束,到没有接收到任何数据的时间,语句发生阻塞
    
  7. 实例代码(只需要关注管道收发业务即可,其它性能监控、数据竞争相关忽略):

    package main
    
    import (
    
      "fmt"
    
    "log"
    
    "net/http"
    
      _ "net/http/pprof"
    
    "os"
    
    "runtime"
    
    "sync"
    
    "sync/atomic"
    
    )
    
    var wg sync.WaitGroup
    
    var timers int64
    
    func main() {
    
      runtime.GOMAXPROCS(1)            
    
      runtime.SetMutexProfileFraction(1) 
    
      runtime.SetBlockProfileRate(1)    
    
      go func() {
    
    
          if err := http.ListenAndServe(":6060", nil); err != nil {
    
            log.Fatal(err)
    
    }
    
          os.Exit(0)
    
    }()
    
      testChannel()
    
      select {}
    
    }
    
    func testChannel()  {
    
      caps := 200000
    
      ch := make(chan int,caps)
    
      wg.Add(caps)
    
      for i:= 0; i < caps; i++ {
    
          go func(a int) {
    
            defer wg.Done()
    
            atomic.AddInt64(&timers, 1)
    
            //原子安全读
    
            if atomic.LoadInt64(&timers) == int64(caps) {
    
                log.Println("close ch========")
    
                defer close(ch)
    
    }
    
            log.Println("索引=",a)
    
            // 通过通道通知main的goroutine
    
            ch <- a
    
            fmt.Println("exit goroutine=",a)
    
          }(i)
    
    }
    
      // 接收数据
    
      for data := range ch {
    
          fmt.Println("channel data = ",data)
    
      }
    
      wg.Wait()
    
      fmt.Println("all done")
    
    }