MST

星途 面试题库

面试题:Go闭包在并发场景下的应用及注意事项

在Go语言的并发编程场景中,闭包经常被使用。请阐述闭包在并发场景下的应用方式,并且说明可能会遇到的问题以及对应的解决方案,同时给出相关代码示例。
21.5万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试

闭包在Go语言并发场景下的应用方式

在Go语言并发场景中,闭包常结合goroutine使用。通过闭包可以将需要在goroutine中执行的逻辑封装起来,并且闭包能够捕获和携带其定义时所在环境的变量,方便在并发执行时使用这些变量。例如,在循环中启动多个goroutine,闭包可以携带循环变量,使每个goroutine能够根据自身携带的变量执行特定逻辑。

可能遇到的问题

  1. 变量共享与竞争条件:当多个goroutine通过闭包访问和修改相同的外部变量时,可能会引发竞争条件(race condition)。这是因为Go语言的并发执行是异步的,多个goroutine可能同时读写同一变量,导致数据不一致。
  2. 闭包对变量的延迟绑定:在循环中创建闭包并启动goroutine时,闭包会延迟绑定循环变量。这意味着所有goroutine可能会使用循环结束时变量的最终值,而不是每个goroutine启动时变量的值。

解决方案

  1. 使用互斥锁(Mutex)解决竞争条件:通过sync.Mutex来保护共享变量,在访问和修改共享变量前加锁,操作完成后解锁,确保同一时间只有一个goroutine能访问共享变量。
  2. 解决闭包延迟绑定问题:可以通过将循环变量作为参数传递给闭包,这样每个闭包就会捕获自己独立的变量副本,而不是共享同一个循环变量。

代码示例

  1. 竞争条件示例及解决
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)
}
  1. 闭包延迟绑定问题示例及解决
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()
}