Golang使用多个协程按顺序打印字母

358 阅读1分钟

今天看到一个有趣的问题,使用协程逐个打印出来“ABCABCABC”,简单尝试了一下,创建三个channel即可,按顺序往每一个channel中发送数据,再从下一个channel里面读取数据。再使用一个channel用于标记是否已经完成

func main() {
   c1 := make(chan int32)
   c2 := make(chan int32)
   c3 := make(chan int32)
   done := make(chan struct{})

   // 协程A,打印A并向协程B发送信号
   go func() {
      count := 0
      for {
         <-c1
         fmt.Print("A")
         c2 <- 1
         count++
         if count >= 3 {
            return
         }
      }
   }()

   // 协程B,打印B并向协程C发送信号
   go func() {
      count := 0
      for {
         <-c2
         fmt.Print("B")
         c3 <- 1
         count++
         if count >= 3 {
            return
         }
      }
   }()

   // 协程C,打印C并向协程A发送信号
   go func() {
      count := 0
      for {
         <-c3
         fmt.Print("C")

         count++
         if count >= 3 {
            return
         }
         c1 <- 1
      }
   }()

   // 启动第一个协程
   c1 <- 1

   // 等待程序结束
   <-done
}

执行完的结果,确实是可以打印出来“ABCABCABC”,但是有一个小问题是,代码行数过多,同时,如果需要我们打印A-Z呢,这样写26遍go func(),估计能崩溃(倒是可以凑上百行代码,哈哈哈哈)

于是需要做一些优化了。

下面是一个很经典的错误写法!

func words() {
 count := 0
 chs := make([]chan bool, 26)
 done := make(chan int)
 for {
  for i := range chs {
   chs[i] = make(chan bool)
   go func(i int) {
    <-chs[i]
    fmt.Printf("%c ", 'A'+i)
    chs[(i+1)%26] <- true
    if i == 25 {
     count++
    }
   }(i)
  }
  if count == 3 {
   <-chs[25]
   done <- 1
   break
  }
 }

 chs[0] <- true
 <-done
}

这个代码会直接进入死循环,卡住。

这里的主要问题是,协程的理解,还是按照python语言的思维方式,在外层用循环,导致不停地创建chan,但是对应的每一个协程都在等待读取,没有进展下去。

修改后的可用代码如下:

func words() {
   chs := make([]chan bool, 26)
   done := make(chan int)
   for i := 0; i < 26; i++ {
      chs[i] = make(chan bool)
      go func(i int) {
         count := 0
         for {
            <-chs[i]
            fmt.Printf("%c ", 'A'+i%26)
            //fmt.Printf("%d", i)
            if i < 25 {
               chs[(i+1)%26] <- true
            }
            count++
            if count >= 3 {
               if i == 25 {
                  done <- 1
               }
               return
            }
            if i == 25 {
               chs[(i+1)%26] <- true
            }

         }

      }(i)
   }

   chs[0] <- true
   <-done
}

简单的理解一下就是,先创建好26个channel,然后给chs[0]写入一个值,让整个流程转起来,然后就等着done即可。