MST

星途 面试题库

面试题:Go语言切片在并发场景下的应用与问题

在并发编程场景中,使用Go语言切片可能会遇到哪些问题?如何解决这些问题?请结合具体的代码示例,描述如何在并发环境中安全地使用切片,并且解释sync包中的哪些工具可以辅助实现这一点。
18.4万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试

使用Go语言切片在并发编程中可能遇到的问题

  1. 数据竞争(Data Race):多个协程同时读写切片时,可能导致数据不一致的情况。例如,一个协程在写入切片时,另一个协程同时读取切片,可能读到部分修改的数据。

解决方法

  1. 使用互斥锁(Mutex)
    • 原理:通过sync.Mutex来保护对切片的读写操作,确保同一时间只有一个协程可以访问切片。
    • 示例代码
package main

import (
    "fmt"
    "sync"
)

var (
    slice []int
    mu    sync.Mutex
)

func addToSlice(num int, wg *sync.WaitGroup) {
    defer wg.Done()
    mu.Lock()
    slice = append(slice, num)
    mu.Unlock()
}

func main() {
    var wg sync.WaitGroup
    for i := 0; i < 10; i++ {
        wg.Add(1)
        go addToSlice(i, &wg)
    }
    wg.Wait()
    fmt.Println(slice)
}
  1. 读写锁(RWMutex)
    • 原理:当读操作远多于写操作时,可以使用sync.RWMutex。允许多个协程同时进行读操作,但写操作时会独占切片,阻止其他读写操作。
    • 示例代码
package main

import (
    "fmt"
    "sync"
)

var (
    slice []int
    rwmu  sync.RWMutex
)

func readSlice(index int, wg *sync.WaitGroup) {
    defer wg.Done()
    rwmu.RLock()
    value := slice[index]
    fmt.Printf("Read value: %d at index %d\n", value, index)
    rwmu.RUnlock()
}

func writeSlice(num int, wg *sync.WaitGroup) {
    defer wg.Done()
    rwmu.Lock()
    slice = append(slice, num)
    rwmu.Unlock()
}

func main() {
    var wg sync.WaitGroup
    for i := 0; i < 5; i++ {
        wg.Add(1)
        go writeSlice(i, &wg)
    }
    for i := 0; i < len(slice); i++ {
        wg.Add(1)
        go readSlice(i, &wg)
    }
    wg.Wait()
}
  1. 使用通道(Channel)
    • 原理:通过通道来安全地传递切片数据,避免直接在多个协程中共享切片。每个协程通过通道接收或发送切片数据,而不是直接读写共享的切片。
    • 示例代码
package main

import (
    "fmt"
    "sync"
)

func worker(input <-chan int, output chan<- []int, wg *sync.WaitGroup) {
    defer wg.Done()
    var localSlice []int
    for num := range input {
        localSlice = append(localSlice, num)
    }
    output <- localSlice
}

func main() {
    var wg sync.WaitGroup
    inputChan := make(chan int)
    outputChan := make(chan []int)

    for i := 0; i < 3; i++ {
        wg.Add(1)
        go worker(inputChan, outputChan, &wg)
    }

    go func() {
        for i := 0; i < 10; i++ {
            inputChan <- i
        }
        close(inputChan)
    }()

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

    var finalSlice []int
    for subSlice := range outputChan {
        finalSlice = append(finalSlice, subSlice...)
    }
    fmt.Println(finalSlice)
}

sync包中辅助实现切片并发安全的工具

  1. sync.Mutex:用于保护对切片的读写操作,通过加锁和解锁确保同一时间只有一个协程可以访问切片。
  2. sync.RWMutex:适用于读多写少的场景,读操作时允许多个协程同时进行,写操作时独占切片,保证数据一致性。
  3. sync.WaitGroup:虽然它本身不直接保护切片,但可以用于等待所有协程完成对切片的操作,确保在所有操作完成后再进行后续处理。