我想尝试FizzBuzz测试(Why can't programmers program),并使用Go。它基本上从1到100循环,并打印" Fizz"当循环计数器可被3整除时," Buzz"当被5整除时," FizzBuzz"当被两者整除时,只需打印数字。
在迭代地和递归地执行之后,我想同时(或通过使用通道)执行此操作。我想出了以下代码,这让我感到惊讶:
func fizzbuzzconc() {
// Channels for communication
fizzchan := make(chan int)
buzzchan := make(chan int)
fizzbuzzchan := make(chan int)
nonechan := make(chan int)
// Start go routine to calculate fizzbuzz challenge
go func() {
for i := 1; i <= 100; i++ {
if i % 3 == 0 && i % 5 == 0 {
fizzbuzzchan <- i
} else if i % 3 == 0 {
fizzchan <- i
} else if i % 5 == 0 {
buzzchan <- i
} else {
nonechan <- i
}
}
}()
// When or how does this for loop end?
for {
select {
case i := <-fizzchan:
fmt.Println(i, "Fizz")
case i := <-buzzchan:
fmt.Println(i, "Buzz")
case i := <-fizzbuzzchan:
fmt.Println(i, "FizzBuzz")
case i := <-nonechan:
fmt.Println(i, i)
}
}
}
我无法理解for循环的停止方式和原因。没有休息条件或退货声明。为什么它最终会完成运行?
答案 0 :(得分:1)
它并没有真正奏效。
经过一段时间后会发生的事情是,由于剩余的常规程序正在等待没有goroutine推动的通道,所以会有一些损耗。所以你所拥有的是一个死锁(这是一个致命的错误,结束了程序),而不是一个干净的结局。
总而言之,&#34;工作&#34;因为go引擎足够智能来检测死锁。
答案 1 :(得分:0)
@dystroy已经很好地回答了您的问题,但是以下是您修复代码的方法。
退出整齐的一种方法是使用退出通道,我们通过关闭它来发出信号。 (通过关闭频道发出信号非常有用,因为不止一个例行程序可以同时收听它。)
还有其他方法可以做到这一点 - 如果你只有一个输出通道,那么你range
通过它来读取结果,并在你完成时close
。你可以轻松地重写这个以这种方式工作。
您可以使用sync.Waitgroup
来确保go例程也已完成。
func main() {
// Channels for communication
fizzchan := make(chan int)
buzzchan := make(chan int)
fizzbuzzchan := make(chan int)
nonechan := make(chan int)
quit := make(chan struct{})
// Start go routine to calculate fizzbuzz challenge
go func() {
for i := 1; i <= 100; i++ {
if i%3 == 0 && i%5 == 0 {
fizzbuzzchan <- i
} else if i%3 == 0 {
fizzchan <- i
} else if i%5 == 0 {
buzzchan <- i
} else {
nonechan <- i
}
}
close(quit)
}()
// When or how does this for loop end?
OUTER:
for {
select {
case i := <-fizzchan:
fmt.Println(i, "Fizz")
case i := <-buzzchan:
fmt.Println(i, "Buzz")
case i := <-fizzbuzzchan:
fmt.Println(i, "FizzBuzz")
case i := <-nonechan:
fmt.Println(i, i)
case <-quit:
break OUTER
}
}
fmt.Println("All done")
}
答案 2 :(得分:0)
@OneOfOne提到了sync.WaitGroup方法,我认为该方法最符合您在Go中执行此操作的方式。考虑到goroutine非常便宜并且问题可以并行解决,我们可以为每个输入创建一个go例程并在缓冲通道上发送结果。
//Size to check
size := 100
results := make(chan Result, size)
// Create the WaitGroup and set the latch size.
var wg sync.WaitGroup
wg.Add(size)
// Create a goroutine for each parallel operation
for i := 1; i <= size; i++ {
i := i //bind value into closure
go func() {
results <- fizzbuzz(i)
wg.Done() //release a latch
}()
}
//wait for all the goroutines to finish.
wg.Wait()
//close the channel so we can exit the below for loop.
close(results)
//Range over the results and exit once the channel has closed.
for x := range results {
fmt.Printf("i: %d result: %s\n", x.Nr, x.Val)
}