MST

星途 面试题库

面试题:Go语言Goroutine任务分解中的数据共享与同步问题

在使用Goroutine进行任务分解时,不可避免会涉及到数据共享。请描述如何使用Go语言的同步原语(如Mutex、Channel等)来确保数据的一致性和避免竞态条件。假设你有一个多Goroutine读写的共享数据结构,如何设计同步机制?
23.3万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试
  1. 使用Mutex
    • 原理:Mutex(互斥锁)是一种简单的同步原语,它在同一时间只允许一个Goroutine访问共享数据。当一个Goroutine获取了Mutex,其他Goroutine必须等待直到该Goroutine释放Mutex。
    • 示例代码
package main

import (
    "fmt"
    "sync"
)

type SharedData struct {
    data int
    mu   sync.Mutex
}

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

func main() {
    var wg sync.WaitGroup
    shared := SharedData{}
    numGoroutines := 10
    for i := 0; i < numGoroutines; i++ {
        wg.Add(1)
        go shared.increment(&wg)
    }
    wg.Wait()
    fmt.Println("Final data:", shared.data)
}
  1. 使用Channel
    • 原理:Channel可以用于在Goroutine之间传递数据,并且它本身是线程安全的。通过将对共享数据的操作封装在发送和接收操作中,可以避免竞态条件。
    • 示例代码
package main

import (
    "fmt"
    "sync"
)

type SharedData struct {
    data int
}

func main() {
    var wg sync.WaitGroup
    shared := SharedData{}
    numGoroutines := 10
    ch := make(chan struct{})
    for i := 0; i < numGoroutines; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            <-ch
            shared.data++
            ch <- struct{}{}
        }()
    }
    ch <- struct{}{}
    wg.Wait()
    close(ch)
    fmt.Println("Final data:", shared.data)
}
  1. 设计同步机制的一般步骤
    • 确定共享数据结构:首先明确哪些数据需要在多个Goroutine之间共享,例如上述示例中的SharedData结构体。
    • 选择合适的同步原语
      • 如果只是简单的读写操作,Mutex通常是一个很好的选择,因为它简单直接,适用于大多数情况。
      • 如果需要在Goroutine之间传递数据并同步操作,Channel可能更合适。
    • 封装操作:将对共享数据的操作封装在函数中,在函数内部使用同步原语来确保数据一致性。例如,在SharedData结构体的increment方法中使用Mutex来保护对data的修改。
    • 测试和验证:使用Go语言内置的go test -race命令来检测是否存在竞态条件,确保同步机制的正确性。