MST

星途 面试题库

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

阐述Go闭包在并发编程中可能会遇到的问题,如竞态条件。假设存在一个闭包,在多个goroutine中被调用,且闭包会修改外部函数中的某个共享变量,如何使用sync包中的工具来避免数据竞争问题,并给出完整的示例代码。
50.4万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试

Go闭包在并发编程中可能遇到的问题

  1. 竞态条件:当多个goroutine同时访问和修改共享变量时,由于执行顺序的不确定性,可能导致数据竞争,最终得到不一致或错误的结果。在闭包中,如果它在多个goroutine中被调用且修改外部函数的共享变量,就很容易出现这种情况。

使用sync包避免数据竞争

  1. 使用sync.Mutexsync.Mutex用于保护共享资源,确保同一时间只有一个goroutine可以访问该资源。

以下是完整的示例代码:

package main

import (
    "fmt"
    "sync"
)

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

    increment := func() {
        defer wg.Done()
        mu.Lock()
        count++
        mu.Unlock()
    }

    for i := 0; i < 10; i++ {
        wg.Add(1)
        go increment()
    }

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

在上述代码中:

  • 定义了一个共享变量count和一个sync.Mutex实例mu
  • increment闭包函数用于增加count的值。在修改count之前,通过mu.Lock()锁定互斥锁,修改完成后通过mu.Unlock()解锁,确保同一时间只有一个goroutine可以修改count
  • 使用sync.WaitGroup来等待所有的goroutine完成。
  1. 使用sync.RWMutex(如果读操作远多于写操作):如果闭包中对共享变量的操作主要是读操作,偶尔有写操作,可以使用sync.RWMutex。它允许多个goroutine同时进行读操作,但写操作时会独占资源。

示例代码如下:

package main

import (
    "fmt"
    "sync"
)

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

    read := func() {
        defer wg.Done()
        mu.RLock()
        fmt.Println("Current count:", count)
        mu.RUnlock()
    }

    write := func() {
        defer wg.Done()
        mu.Lock()
        count++
        mu.Unlock()
    }

    for i := 0; i < 5; i++ {
        wg.Add(1)
        go read()
    }

    for i := 0; i < 2; i++ {
        wg.Add(1)
        go write()
    }

    wg.Wait()
}

在这个代码中,read函数使用mu.RLock()mu.RUnlock()进行读锁定和解锁,允许多个goroutine同时读取countwrite函数使用mu.Lock()mu.Unlock()进行写锁定和解锁,确保写操作的原子性。