最近遇到一个程序设计题,这里分享下。
当时遇到后,觉得应该是用到 channel 但对于具体怎样用,当时一时没想到怎么实现,这里记录下通过 多channel + mod运算 + sync.WaitGroup 实现。
下面是通过大模型给出的答案,供参考。
在Go语言中,确保goroutine按特定顺序执行(比如,依次打印"cat", "fish", "dog")是一个常见的同步问题。由于goroutine的执行是并发的,我们需要使用某种同步机制来控制它们的执行顺序。一种常见的方法是使用通道(channel)和等待组(sync.WaitGroup)来同步goroutine的执行。但是,为了按照特定顺序执行,仅使用sync.WaitGroup是不够的,因为sync.WaitGroup只能用来等待一组goroutine完成,而不能确保它们以特定顺序执行。
这里,我们可以使用带缓冲的通道来同步这些打印操作。具体来说,我们可以创建一个包含三个通道的数组,每个通道代表一个阶段,每个阶段只有当前阶段的goroutine能写入,下一个阶段的goroutine能读取。通过这种方式,我们可以确保打印操作按照"cat", "fish", "dog"的顺序进行。
以下是一个实现示例:
package main
import (
"fmt"
"sync"
)
func main() {
var wg sync.WaitGroup
// 创建三个带缓冲的通道
stages := [3]chan bool{make(chan bool, 1), make(chan bool, 1), make(chan bool, 1)}
// 初始化第一个阶段,使其可以直接开始
stages[0] <- true
// 启动goroutine
for i := 0; i < 3; i++ {
wg.Add(1)
go func(i int, stages [3]chan bool) {
defer wg.Done()
var animal string
switch i {
case 0:
animal = "cat"
case 1:
animal = "fish"
case 2:
animal = "dog"
}
for j := 0; j < 100; j++ {
<-stages[i] // 等待前一个阶段完成
fmt.Println(animal)
stages[(i+1)%3] <- true // 通知下一个阶段可以开始
}
}(i, stages)
}
wg.Wait() // 等待所有goroutine完成
close(stages[0]) // 关闭通道,实际上在这个例子中不需要,因为我们不会再读取它们
close(stages[1])
close(stages[2])
}
注意:
- 我们使用了一个
[3]chan bool的数组stages,其中每个元素都是一个带有一个缓冲区的通道。这些通道用于在不同的goroutine之间进行同步。 - 每个goroutine在开始打印前都会从它自己的通道中接收一个值(这里使用
true作为占位符),这确保了它只在接收到信号时才开始执行。 - 在完成打印后,goroutine会向下一个阶段的通道发送一个信号(即发送
true),以便下一个goroutine可以开始执行。 - 由于有三个阶段,我们使用
(i+1)%3来计算下一个阶段的索引,以确保索引在循环时能够正确回绕。 - 使用
sync.WaitGroup来等待所有goroutine完成。
这种方法确保了即使在不同的goroutine中,打印操作也会按照"cat", "fish", "dog"的顺序循环进行。