MST

星途 面试题库

面试题:Go中Mutex锁在多协程读写场景下的最佳实践应用

在Go语言中,假设你有一个多协程读写共享数据的场景,数据结构为一个简单的整数变量。请描述如何使用Mutex锁来保证数据的一致性,并给出相应的代码示例。同时说明在读写操作频繁时,这种方式可能存在的性能问题。
28.4万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试

使用Mutex锁保证数据一致性及代码示例

在Go语言中,sync.Mutex 是用于实现互斥锁的结构体,通过它可以保证在同一时间只有一个协程能够访问共享数据,从而确保数据的一致性。以下是使用 sync.Mutex 保护简单整数变量的代码示例:

package main

import (
    "fmt"
    "sync"
)

var (
    num  int
    mutex sync.Mutex
)

func read(wg *sync.WaitGroup) {
    defer wg.Done()
    mutex.Lock()
    fmt.Printf("Read value: %d\n", num)
    mutex.Unlock()
}

func write(wg *sync.WaitGroup, value int) {
    defer wg.Done()
    mutex.Lock()
    num = value
    fmt.Printf("Write value: %d\n", num)
    mutex.Unlock()
}

func main() {
    var wg sync.WaitGroup

    // 启动写操作协程
    wg.Add(1)
    go write(&wg, 42)

    // 启动读操作协程
    wg.Add(1)
    go read(&wg)

    wg.Wait()
}

在上述代码中:

  1. 定义了一个全局的整数变量 num 和一个 sync.Mutex 类型的变量 mutex
  2. read 函数用于读取 num 的值,在读取前调用 mutex.Lock() 锁定互斥锁,读取完成后调用 mutex.Unlock() 解锁互斥锁。
  3. write 函数用于写入 num 的值,同样在写入前后分别锁定和解锁互斥锁。
  4. main 函数中,启动了一个写操作协程和一个读操作协程,并使用 sync.WaitGroup 等待所有协程完成。

读写操作频繁时存在的性能问题

  1. 锁竞争开销:当读写操作非常频繁时,大量的协程会竞争 Mutex 锁。获取和释放锁的操作本身会带来一定的CPU开销,随着竞争的加剧,这种开销会变得更加显著,从而降低系统的整体性能。
  2. 读写阻塞:由于 Mutex 锁是排他锁,在一个协程获取锁进行写操作时,其他所有读和写操作的协程都必须等待锁的释放。这可能导致读操作的延迟增加,尤其是在写操作比较频繁的情况下,读操作可能会长时间等待锁,影响系统的响应速度。
  3. 吞吐量下降:由于锁竞争和阻塞,系统的并发性能得不到充分发挥,整体的吞吐量会随着读写操作频率的增加而下降。特别是在多核CPU环境下,由于 Mutex 锁会限制同一时间只有一个协程访问共享数据,无法充分利用多核的并行计算能力。