面试题答案
一键面试原因解释
在这段代码中,for
循环创建了多个goroutine,但是它们都引用了同一个num
变量。由于goroutine是并发执行的,在fmt.Println(num)
执行时,for
循环可能已经结束,num
的值已经变成了5
,所以可能输出多个5
,而不是预期的1 2 3 4 5
。这是因为num
是在循环外部声明的,所有goroutine共享这个变量。
优化方案
package main
import (
"fmt"
"sync"
)
func main() {
var wg sync.WaitGroup
numbers := []int{1, 2, 3, 4, 5}
for _, num := range numbers {
wg.Add(1)
go func(n int) {
defer wg.Done()
fmt.Println(n)
}(num)
}
wg.Wait()
}
在这个优化版本中,通过将num
作为参数传递给匿名函数,每个goroutine都有了自己独立的变量副本,从而确保了按顺序输出1 2 3 4 5
。
匿名函数的作用
匿名函数在这里起到了封装代码块的作用,使得每个goroutine可以独立执行特定的任务。通过将num
作为参数传递给匿名函数,避免了所有goroutine共享同一个变量带来的问题。
并发原语的原理
sync.WaitGroup
:sync.WaitGroup
用于等待一组goroutine完成。wg.Add(1)
表示增加等待组的计数,wg.Done()
表示减少等待组的计数,wg.Wait()
会阻塞当前goroutine,直到等待组的计数为0,即所有相关的goroutine都调用了wg.Done()
。在这个例子中,wg.Add(1)
为每个新创建的goroutine增加计数,wg.Done()
在goroutine结束时减少计数,wg.Wait()
确保main
函数等待所有goroutine执行完毕。