MST

星途 面试题库

面试题:Go多值返回在并发场景下可能会遇到哪些问题及如何解决?

在并发编程中使用Go的多值返回函数时,可能会出现诸如竞争条件、数据不一致等问题。请详细阐述可能遇到的具体问题场景,并给出相应的解决方案,包括使用同步原语、channel等方式的设计思路和代码示例。
28.5万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试

可能遇到的问题场景

  1. 竞争条件:多个 goroutine 同时访问和修改共享变量。例如在一个银行转账操作中,多个 goroutine 可能同时读取账户余额,并基于旧的余额进行扣除操作,导致数据不一致。
  2. 数据不一致:当一个 goroutine 修改了共享数据,但其他 goroutine 未及时感知到这种变化,就会出现数据不一致。比如一个缓存更新操作,一个 goroutine 更新了缓存,但另一个 goroutine 仍然从旧的缓存数据读取。

解决方案

  1. 使用同步原语 - mutex(互斥锁)
    • 设计思路:通过在访问共享资源前加锁,访问完后解锁,确保同一时间只有一个 goroutine 能访问共享资源。
    • 代码示例
package main

import (
    "fmt"
    "sync"
)

var (
    counter int
    mu      sync.Mutex
)

func increment(wg *sync.WaitGroup) {
    defer wg.Done()
    mu.Lock()
    counter++
    mu.Unlock()
}

func main() {
    var wg sync.WaitGroup
    for i := 0; i < 10; i++ {
        wg.Add(1)
        go increment(&wg)
    }
    wg.Wait()
    fmt.Println("Final counter value:", counter)
}
  1. 使用 channel
    • 设计思路:通过 channel 进行数据传递,避免共享变量。goroutine 之间通过发送和接收数据来同步操作,确保数据一致性。
    • 代码示例
package main

import (
    "fmt"
)

func worker(id int, jobs <-chan int, results chan<- int) {
    for j := range jobs {
        fmt.Printf("Worker %d started job %d\n", id, j)
        result := j * 2
        fmt.Printf("Worker %d finished job %d, result: %d\n", id, j, result)
        results <- result
    }
}

func main() {
    const numJobs = 5
    jobs := make(chan int, numJobs)
    results := make(chan int, numJobs)

    for w := 1; w <= 3; w++ {
        go worker(w, jobs, results)
    }

    for j := 1; j <= numJobs; j++ {
        jobs <- j
    }
    close(jobs)

    for a := 1; a <= numJobs; a++ {
        <-results
    }
    close(results)
}
  1. 使用 sync.RWMutex(读写互斥锁)
    • 设计思路:适用于读多写少的场景。允许多个 goroutine 同时读共享资源,但写操作时需要独占锁,防止读写冲突和写写冲突。
    • 代码示例
package main

import (
    "fmt"
    "sync"
)

var (
    data  = make(map[string]int)
    rwmu  sync.RWMutex
)

func read(key string, wg *sync.WaitGroup) {
    defer wg.Done()
    rwmu.RLock()
    value, exists := data[key]
    rwmu.RUnlock()
    if exists {
        fmt.Printf("Read key %s, value: %d\n", key, value)
    } else {
        fmt.Printf("Key %s not found\n", key)
    }
}

func write(key string, value int, wg *sync.WaitGroup) {
    defer wg.Done()
    rwmu.Lock()
    data[key] = value
    rwmu.Unlock()
    fmt.Printf("Write key %s, value: %d\n", key, value)
}

func main() {
    var wg sync.WaitGroup

    wg.Add(1)
    go write("key1", 100, &wg)

    for i := 0; i < 3; i++ {
        wg.Add(1)
        go read("key1", &wg)
    }

    wg.Wait()
}