select...case的使用

package main

import (
   "fmt"
   "time"
)

func main() {
   intChan1 := make(chan int)
   intChan2 := make(chan int)
   intChan3 := make(chan int)

   // 协程1
   go func() {
      for i1 := 0; i1 < 5; i1++ {
         intChan1 <- i1 // 向管道写入数据
         time.Sleep(time.Second)
         <-intChan1 // 从管道读出数据
      }
   }()

   // 协程2
   go func() {
      for i2 := 0; i2 < 5; i2++ {
         intChan2 <- i2 // 向管道写入数据
         time.Sleep(time.Second)
         <-intChan2 // 从管道读出数据
      }
   }()

   // 协程3
   go func() {
      for i3 := 0; i3 < 5; i3++ {
         intChan3 <- i3 // 向管道写入数据
         time.Sleep(time.Second)
         <-intChan3 // 从管道读出数据
      }
   }()

   // 适用死循环监控管道的读写情况
   for {
      select {
      // 监控三个管道的写入情况
      case i, ok := <-intChan1:
         if ok {
            fmt.Printf("监控到『管道1』写入了数据『%+v\n", i)
         } else {
            fmt.Printf("『管道1』已关闭\n")
         }
      case i, ok := <-intChan2:
         if ok {
            fmt.Printf("监控到『管道2』写入了数据『%+v\n", i)
         } else {
            fmt.Printf("『管道2』已关闭\n")
         }
      case i, ok := <-intChan3:
         if ok {
            fmt.Printf("监控到『管道3』写入了数据『%+v\n", i)
         } else {
            fmt.Printf("『管道3』已关闭\n")
         }

      // 监控三个管道的读取情况
      case intChan1 <- 0:
         fmt.Printf("监控到『管道1』读出了数据 \n")
      case intChan2 <- 0:
         fmt.Printf("监控到『管道2』读出了数据 \n")
      case intChan3 <- 0:
         fmt.Printf("监控到『管道3』读出了数据 \n")

      // 强烈建议设置default,避免出现deadlock,哪怕default里不做任何操作
      default:
      }
   }
}

// ========== 输出过程·开始 ========== //
// 监控到『管道1』写入了数据『0』
// 监控到『管道2』写入了数据『0』
// 监控到『管道3』写入了数据『0』
// 监控到『管道2』读出了数据
// 监控到『管道1』读出了数据
// 监控到『管道2』写入了数据『1』
// 监控到『管道1』写入了数据『1』
// 监控到『管道3』读出了数据
// 监控到『管道3』写入了数据『1』
// 监控到『管道1』读出了数据
// 监控到『管道2』读出了数据
// 监控到『管道1』写入了数据『2』
// 监控到『管道2』写入了数据『2』
// 监控到『管道3』读出了数据
// 监控到『管道3』写入了数据『2』
// 监控到『管道2』读出了数据
// 监控到『管道1』读出了数据
// 监控到『管道2』写入了数据『3』
// 监控到『管道3』读出了数据
// 监控到『管道1』写入了数据『3』
// 监控到『管道3』写入了数据『3』
// 监控到『管道1』读出了数据
// 监控到『管道2』读出了数据
// 监控到『管道1』写入了数据『4』
// 监控到『管道2』写入了数据『4』
// 监控到『管道3』读出了数据
// 监控到『管道3』写入了数据『4』
// 监控到『管道1』读出了数据
// 监控到『管道2』读出了数据
// 监控到『管道3』读出了数据
// ▎(阻塞中)
// ========== 输出过程·开始 ========== //

// ========== 总结 ========== //
// 1、select-case和switch-case在形式上非常像,select主要使用场景就是监听管道的读写(IO),可以说是管道版的switch。
// 2、select里的case是随机执行的,而不像switch里的case是顺序执行的,若匹配到多个case只会随机执行其中一个,其余的不会执行。
// 3、如果没有匹配到case且没有设置default就会发生deadlock,所以必须设置default,哪怕不做任何操作,以避免出现deadlock问题。
// 4、select-case本身没有循环效果,所以通常需要配合for死循环使用实现监听管道的读写(IO),并且case里面可以使用break关键字,
//    需要注意的是break的目标是select,而不是for死循环。

Copyright © 2024 码农人生. All Rights Reserved