面试题答案
一键面试使用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()
}
在上述代码中:
- 定义了一个全局的整数变量
num
和一个sync.Mutex
类型的变量mutex
。 read
函数用于读取num
的值,在读取前调用mutex.Lock()
锁定互斥锁,读取完成后调用mutex.Unlock()
解锁互斥锁。write
函数用于写入num
的值,同样在写入前后分别锁定和解锁互斥锁。- 在
main
函数中,启动了一个写操作协程和一个读操作协程,并使用sync.WaitGroup
等待所有协程完成。
读写操作频繁时存在的性能问题
- 锁竞争开销:当读写操作非常频繁时,大量的协程会竞争
Mutex
锁。获取和释放锁的操作本身会带来一定的CPU开销,随着竞争的加剧,这种开销会变得更加显著,从而降低系统的整体性能。 - 读写阻塞:由于
Mutex
锁是排他锁,在一个协程获取锁进行写操作时,其他所有读和写操作的协程都必须等待锁的释放。这可能导致读操作的延迟增加,尤其是在写操作比较频繁的情况下,读操作可能会长时间等待锁,影响系统的响应速度。 - 吞吐量下降:由于锁竞争和阻塞,系统的并发性能得不到充分发挥,整体的吞吐量会随着读写操作频率的增加而下降。特别是在多核CPU环境下,由于
Mutex
锁会限制同一时间只有一个协程访问共享数据,无法充分利用多核的并行计算能力。