channel 相关知识点


1
type hchan struct { 2 qcount uint // 队列中元素数量 3 dataqsiz uint // 环形队列大小 4 buf unsafe.Pointer // 指向环形队列 5 elemsize uint16 // 元素大小 6 closed uint32 // 关闭标志 7 elemtype *_type // 元素类型 8 sendx uint // 发送索引 9 recvx uint // 接收索引 10 recvq waitq // 接收等待队列 11 sendq waitq // 发送等待队列 12 lock mutex // 互斥锁 13 }

关键操作原理

  1. 发送流程

    • 加锁保护

    • 如果有接收者等待,直接拷贝数据给接收者

    • 否则尝试放入缓冲区

    • 缓冲区满则加入sendq并挂起

  2. 接收流程

    • 加锁保护

    • 如果有发送者等待,直接从发送者获取数据

    • 否则尝试从缓冲区获取

    • 缓冲区空则加入recvq并挂起

    • // 1. 同步通信(无缓冲)
      ch := make(chan struct{})
      go func() {
          // ... 工作
          ch <- struct{}{} // 发送完成信号
      }()
      <-ch // 等待完成
      
      // 2. 异步通信(有缓冲)
      ch := make(chan int, 100)
      go func() {
          for task := range ch {
              // 处理任务
          }
      }()

      高级模式:

    • // 1. 扇出(一个生产者,多个消费者)
      func fanOut(in <-chan int, out []chan int) {
          for v := range in {
              for _, ch := range out {
                  ch <- v
              }
          }
      }
      
      // 2. 扇入(多个生产者,一个消费者)
      func fanIn(out chan<- int, ins ...<-chan int) {
          var wg sync.WaitGroup
          for _, in := range ins {
              wg.Add(1)
              go func(in <-chan int) {
                  defer wg.Done()
                  for v := range in {
                      out <- v
                  }
              }(in)
          }
          wg.Wait()
      }
      
      // 3. 超时控制
      select {
      case res := <-ch:
          fmt.Println(res)
      case <-time.After(1 * time.Second):
          fmt.Println("timeout")
      }

      4. Channel 操作特性

      操作类型与结果

      操作nil channelclosed channelactive channel
      发送 永久阻塞 panic 阻塞或成功
      接收 永久阻塞 不阻塞返回零值 阻塞或成功
      关闭 panic panic 成功

      select 机制

      • 随机选择一个就绪的case执行

      • 可设置default避免阻塞

      • 典型应用:

        • 超时控制

        • 非阻塞操作

        • 多channel监听

      5. Channel 常见问题

      1. Channel 泄漏

      场景

      • Goroutine阻塞在channel操作无法退出

      • 未被引用的channel占用内存

      预防

      • 使用context控制退出

      • 确保所有Goroutine都有退出路径

      • 使用buffered channel减少阻塞

      2. Panic 场景

      • 关闭nil channel

      • 重复关闭channel

      • 向已关闭channel发送数据

      3. 性能考量

      • 无缓冲channel有更高的同步开销

      • 大量小对象传输考虑传递指针

      • 高并发场景考虑sync.Pool减少分配

      6. Channel 与并发控制

      并发模式对比

      模式Channelsync.Mutexsync.WaitGroup
      数据传递
      状态共享 适合 适合 有限
      同步控制 适合 适合 适合
      复杂度

      选择建议

      • 用channel

        • 数据传输

        • 事件通知

        • 控制流水线

      • 用锁

        • 保护共享状态

        • 精细控制临界区

      • 用WaitGroup

        • 简单等待任务组完成工作池模式

        •  

          func workerPool(numWorkers int, jobs <-chan int, results chan<- int) {
              var wg sync.WaitGroup
              for i := 0; i < numWorkers; i++ {
                  wg.Add(1)
                  go func(id int) {
                      defer wg.Done()
                      for job := range jobs {
                          results <- process(job) // 处理任务
                      }
                  }(i)
              }
              wg.Wait()
              close(results)
          }

          优雅关闭

        • func runService(stopCh <-chan struct{}) {
              for {
                  select {
                  case <-stopCh:
                      fmt.Println("shutting down...")
                      return
                  case data := <-inputCh:
                      go handle(data)
                  }
              }
          }
          
          // 调用方
          close(stopCh) // 广播关闭信号

           

posted @ 2025-04-30 15:43  尹少爷  阅读(36)  评论(0)    收藏  举报