MST

星途 面试题库

面试题:Go实参到形参传递与并发场景结合

在Go的并发编程场景下,有一个函数接收一个共享的结构体作为参数,多个协程可能同时调用这个函数。由于Go函数参数传递机制,在这种情况下可能会出现什么数据竞争问题?如何通过合理的参数传递方式和同步机制(如互斥锁、通道等)来避免这些问题?请给出具体的代码示例和解释。
36.5万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试
  1. 可能出现的数据竞争问题
    • 由于Go函数参数传递是值传递,虽然结构体本身是值传递,但结构体内部的指针字段可能会指向共享内存。当多个协程同时调用该函数并通过结构体指针字段访问或修改共享数据时,就可能出现数据竞争。例如,结构体中有一个指向共享切片的指针,多个协程同时对该切片进行读写操作,就会导致数据不一致。
  2. 使用互斥锁避免数据竞争
package main

import (
    "fmt"
    "sync"
)

// 定义共享结构体
type SharedStruct struct {
    Data int
}

var mu sync.Mutex

// 接收共享结构体的函数
func processSharedStruct(s *SharedStruct) {
    mu.Lock()
    defer mu.Unlock()
    // 对共享数据进行操作
    s.Data++
    fmt.Println("Processed data:", s.Data)
}

func main() {
    var wg sync.WaitGroup
    shared := SharedStruct{Data: 0}

    for i := 0; i < 5; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            processSharedStruct(&shared)
        }()
    }
    wg.Wait()
}
  • 解释
    • 定义了一个共享结构体SharedStruct,包含一个Data字段。
    • 使用sync.Mutex来保护对SharedStruct数据的访问。在processSharedStruct函数中,通过mu.Lock()获取锁,在函数结束时通过defer mu.Unlock()释放锁,这样确保同一时间只有一个协程能访问和修改SharedStruct中的数据,避免了数据竞争。
  1. 使用通道避免数据竞争
package main

import (
    "fmt"
    "sync"
)

// 定义共享结构体
type SharedStruct struct {
    Data int
}

func processSharedStruct(ch <-chan *SharedStruct, wg *sync.WaitGroup) {
    defer wg.Done()
    for s := range ch {
        // 对共享数据进行操作
        s.Data++
        fmt.Println("Processed data:", s.Data)
    }
}

func main() {
    var wg sync.WaitGroup
    shared := SharedStruct{Data: 0}
    ch := make(chan *SharedStruct)

    for i := 0; i < 5; i++ {
        wg.Add(1)
        go processSharedStruct(ch, &wg)
    }

    for i := 0; i < 5; i++ {
        ch <- &shared
    }
    close(ch)
    wg.Wait()
}
  • 解释
    • 同样定义了共享结构体SharedStruct
    • 创建一个通道ch,类型为*SharedStruct指针类型。协程从通道中接收结构体指针并操作。
    • main函数中,通过ch <- &shared将共享结构体指针发送到通道,协程从通道接收并处理。通道本身是线程安全的,通过这种方式,同一时间只有一个协程能处理从通道接收到的共享结构体,从而避免数据竞争。当发送完所有数据后,通过close(ch)关闭通道,使得协程的for... range循环结束。