面试题答案
一键面试闭包在Go语言并发场景下的应用方式
在Go语言并发场景中,闭包常结合goroutine
使用。通过闭包可以将需要在goroutine
中执行的逻辑封装起来,并且闭包能够捕获和携带其定义时所在环境的变量,方便在并发执行时使用这些变量。例如,在循环中启动多个goroutine
,闭包可以携带循环变量,使每个goroutine
能够根据自身携带的变量执行特定逻辑。
可能遇到的问题
- 变量共享与竞争条件:当多个
goroutine
通过闭包访问和修改相同的外部变量时,可能会引发竞争条件(race condition)。这是因为Go语言的并发执行是异步的,多个goroutine
可能同时读写同一变量,导致数据不一致。 - 闭包对变量的延迟绑定:在循环中创建闭包并启动
goroutine
时,闭包会延迟绑定循环变量。这意味着所有goroutine
可能会使用循环结束时变量的最终值,而不是每个goroutine
启动时变量的值。
解决方案
- 使用互斥锁(Mutex)解决竞争条件:通过
sync.Mutex
来保护共享变量,在访问和修改共享变量前加锁,操作完成后解锁,确保同一时间只有一个goroutine
能访问共享变量。 - 解决闭包延迟绑定问题:可以通过将循环变量作为参数传递给闭包,这样每个闭包就会捕获自己独立的变量副本,而不是共享同一个循环变量。
代码示例
- 竞争条件示例及解决
package main
import (
"fmt"
"sync"
)
var (
counter int
mu sync.Mutex
)
func main() {
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1)
go func() {
defer wg.Done()
mu.Lock()
counter++
mu.Unlock()
}()
}
wg.Wait()
fmt.Println("Final counter:", counter)
}
- 闭包延迟绑定问题示例及解决
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)
// 传递num作为参数,避免延迟绑定
go func(n int) {
defer wg.Done()
fmt.Println("Number:", n)
}(num)
}
wg.Wait()
}