MST

星途 面试题库

面试题:Go闭包与匿名函数在并发场景下的协同问题

假设你在Go语言的并发编程场景中使用闭包和匿名函数,现有一个需求:创建多个goroutine,每个goroutine使用闭包来处理数据,并将结果汇总。但在实际运行中发现数据竞争问题,请分析可能出现问题的原因,并给出解决方案,同时说明闭包和匿名函数在这个并发场景中的行为特点。
15.2万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试

问题原因分析

  1. 共享变量未保护:在闭包内,goroutine可能共享某些变量,而这些变量在并发读写时没有适当的同步机制,从而导致数据竞争。例如,多个goroutine可能同时修改同一个全局变量或者在闭包中引用的外层作用域变量,没有加锁保护。
  2. 闭包对变量的引用特性:闭包会引用外部作用域的变量,当多个goroutine并发执行闭包时,可能因为对同一个外部变量的引用,在不同时间点修改该变量,造成数据不一致。

解决方案

  1. 使用互斥锁(Mutex)
package main

import (
    "fmt"
    "sync"
)

func main() {
    var mu sync.Mutex
    var result int
    var wg sync.WaitGroup

    for i := 0; i < 10; i++ {
        wg.Add(1)
        go func(num int) {
            defer wg.Done()
            mu.Lock()
            result += num
            mu.Unlock()
        }(i)
    }

    wg.Wait()
    fmt.Println("Final result:", result)
}

这里使用sync.Mutex来保护result变量,确保同一时间只有一个goroutine可以修改它。 2. 使用读写锁(RWMutex):如果读操作远多于写操作,可以使用读写锁。读操作时多个goroutine可以同时进行,写操作时会独占资源。

package main

import (
    "fmt"
    "sync"
)

func main() {
    var mu sync.RWMutex
    var result int
    var wg sync.WaitGroup

    for i := 0; i < 10; i++ {
        if i%2 == 0 {
            wg.Add(1)
            go func() {
                defer wg.Done()
                mu.RLock()
                _ = result
                mu.RUnlock()
            }()
        } else {
            wg.Add(1)
            go func(num int) {
                defer wg.Done()
                mu.Lock()
                result += num
                mu.Unlock()
            }(i)
        }
    }

    wg.Wait()
    fmt.Println("Final result:", result)
}
  1. 使用通道(Channel):通过通道在goroutine之间传递数据,避免共享变量。
package main

import (
    "fmt"
    "sync"
)

func main() {
    resultChan := make(chan int)
    var wg sync.WaitGroup

    for i := 0; i < 10; i++ {
        wg.Add(1)
        go func(num int) {
            defer wg.Done()
            resultChan <- num
        }(i)
    }

    go func() {
        wg.Wait()
        close(resultChan)
    }()

    var result int
    for val := range resultChan {
        result += val
    }

    fmt.Println("Final result:", result)
}

闭包和匿名函数在并发场景中的行为特点

  1. 变量引用:闭包会捕获并引用外部作用域的变量,在并发场景中,这意味着多个goroutine可能引用并操作同一个外部变量,容易引发数据竞争问题。
  2. 延迟绑定:闭包内对外部变量的引用是延迟绑定的,即闭包执行时才会获取变量的值。这在并发场景中可能导致意外结果,例如在循环中创建闭包,多个闭包可能引用到循环结束后变量的最终值,而不是每次循环时变量的当前值。
  3. 独立执行上下文:每个由匿名函数启动的goroutine都有独立的执行上下文,但是闭包共享外部变量,所以要特别注意变量的并发访问问题。