当前位置: 技术文章>> 100道Go语言面试题之-在Go中,如何实现协程(goroutine)之间的同步?

文章标题:100道Go语言面试题之-在Go中,如何实现协程(goroutine)之间的同步?
  • 文章分类: 后端
  • 3482 阅读

在Go语言中,协程(goroutine)之间的同步主要通过几种机制来实现,包括通道(channels)、互斥锁(sync.Mutex)、读写互斥锁(sync.RWMutex)、以及条件变量(sync.Cond,虽然较少直接使用,但在某些特定场景下很有用)。下面将详细介绍这些机制:

1. 通道(Channels)

通道是Go语言中最常用的协程间通信和同步的机制。通过发送和接收操作,协程可以在通道上进行阻塞等待,从而实现同步。

ch := make(chan int)

go func() {
    // 模拟耗时操作
    time.Sleep(1 * time.Second)
    ch <- 1 // 发送数据到通道,如果通道已满,则等待直到有空间
}()

// 等待接收通道中的数据
val := <-ch // 如果通道为空,则等待直到有数据
fmt.Println(val)

2. 互斥锁(sync.Mutex)

互斥锁用于保护共享资源,确保同一时间只有一个协程可以访问该资源。

var (
    mu sync.Mutex
    data int
)

func updateData(n int) {
    mu.Lock() // 加锁
    defer mu.Unlock() // 解锁,确保即使在发生panic时也能释放锁
    data += n
}

// 可以在多个goroutine中调用updateData

3. 读写互斥锁(sync.RWMutex)

读写互斥锁是互斥锁的一个变种,允许多个协程同时读取共享资源,但写入操作是互斥的。

var (
    rwMu sync.RWMutex
    data int
)

func readData() int {
    rwMu.RLock() // 加读锁
    defer rwMu.RUnlock() // 解锁
    return data
}

func updateData(n int) {
    rwMu.Lock() // 加写锁
    defer rwMu.Unlock() // 解锁
    data += n
}

// 可以在多个goroutine中调用readData和updateData

4. 条件变量(sync.Cond)

条件变量依赖于互斥锁,用于在协程之间协调条件等待和条件通知。条件变量比简单的通道提供了更灵活的等待/通知机制。

var (
    mu    sync.Mutex
    cond  *sync.Cond
    ready bool
)

func init() {
    cond = sync.NewCond(&mu)
}

func waitForReady() {
    mu.Lock()
    defer mu.Unlock()
    for !ready {
        cond.Wait() // 等待条件满足
    }
    // 条件满足,执行后续操作
}

func setReady() {
    mu.Lock()
    defer mu.Unlock()
    ready = true
    cond.Signal() // 通知一个等待的协程
    // 或者使用cond.Broadcast()通知所有等待的协程
}

// 可以在多个goroutine中调用waitForReady和setReady

总结

Go语言通过通道、互斥锁、读写互斥锁和条件变量等机制提供了丰富的协程间同步手段。开发者可以根据具体场景和需求选择最合适的同步机制。在多数情况下,通道因其简洁性和内置于语言的特性,是协程间通信和同步的首选方式。

推荐文章