MST

星途 面试题库

面试题:Go语言匿名函数在并发场景下的陷阱与优化

在Go语言并发编程中,我们经常会使用匿名函数配合goroutine。假设有如下代码: ```go 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() { defer wg.Done() fmt.Println(num) }() } wg.Wait() } ``` 这段代码输出的结果可能并不是我们预期的`1 2 3 4 5`,请解释原因,并给出优化方案,确保按顺序输出`1 2 3 4 5`。同时,说明在这个优化过程中,匿名函数的作用以及可能涉及到的并发原语的原理。
22.2万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试

原因解释

在这段代码中,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共享同一个变量带来的问题。

并发原语的原理

  1. sync.WaitGroupsync.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执行完毕。