MST
星途 面试题库

面试题:Go中如何在goroutine中安全地共享数据

请描述在Go语言的goroutine中,有哪些方法可以安全地共享数据,并简要说明每种方法的适用场景与基本原理。
32.8万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试

在Go语言的goroutine中,以下是几种安全共享数据的方法:

1. 互斥锁(sync.Mutex)

  • 适用场景:适用于读写操作都存在,且对数据一致性要求较高的场景,防止多个goroutine同时访问共享资源。
  • 基本原理:互斥锁通过加锁(Lock)和解锁(Unlock)操作来保证在同一时刻只有一个goroutine能够访问共享资源。当一个goroutine调用Lock方法获取锁时,如果锁已被其他goroutine持有,该goroutine会被阻塞,直到锁被释放。

示例代码:

package main

import (
    "fmt"
    "sync"
)

var (
    mu    sync.Mutex
    count int
)

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

2. 读写锁(sync.RWMutex)

  • 适用场景:适用于读操作远多于写操作的场景。读操作可以并发执行,写操作则需要独占访问。
  • 基本原理:读写锁区分了读锁(RLock)和写锁(Lock)。多个goroutine可以同时获取读锁进行读操作,因为读操作不会修改数据,不会产生数据竞争。但是当一个goroutine获取写锁时,其他所有读、写操作都被阻塞,直到写锁被释放。

示例代码:

package main

import (
    "fmt"
    "sync"
)

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

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

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

3. Channel

  • 适用场景:适用于通过消息传递方式来共享数据,避免直接共享内存。特别适用于不同goroutine之间需要进行数据传递和同步的场景。
  • 基本原理:Channel是一种类型安全的管道,用于在goroutine之间发送和接收数据。数据通过Channel在不同goroutine之间传递,而不是直接共享内存。发送(<-chan)和接收(chan<-)操作是同步的,确保数据的一致性。

示例代码:

package main

import (
    "fmt"
)

func producer(ch chan int) {
    for i := 0; i < 5; i++ {
        ch <- i
    }
    close(ch)
}

func consumer(ch chan int) {
    for value := range ch {
        fmt.Println("Received:", value)
    }
}

4. sync/atomic包

  • 适用场景:适用于对基本数据类型(如int32、int64、uint32、uint64等)进行原子操作,在性能要求较高且操作简单的场景下使用。
  • 基本原理:atomic包提供了一系列原子操作函数,这些操作在硬件层面保证了操作的原子性,即在执行过程中不会被其他goroutine打断,从而避免数据竞争。

示例代码:

package main

import (
    "fmt"
    "sync"
    "sync/atomic"
)

var counter int64

func increment(wg *sync.WaitGroup) {
    defer wg.Done()
    atomic.AddInt64(&counter, 1)
}