面试题答案
一键面试实现原理差异
- Mutex(互斥锁):Mutex是一种简单的同步原语,用于保证同一时间只有一个goroutine可以访问共享资源。它通过一个状态位来表示锁的状态,当一个goroutine获取锁时,会将状态位设置为锁定状态,其他goroutine尝试获取锁时会阻塞,直到锁被释放。
- sync.RWMutex(读写锁):读写锁区分了读操作和写操作。允许多个goroutine同时进行读操作,因为读操作不会修改共享资源,不会产生数据竞争。而写操作需要独占访问,以防止数据不一致。读写锁内部维护了两个计数器,一个用于记录当前正在进行的读操作数量,另一个用于表示是否有写操作正在进行或等待。
性能表现差异
- Mutex:在读写操作频繁且写操作较多的场景下,Mutex会导致较高的性能开销,因为每次读写操作都需要获取锁,其他goroutine都要等待锁的释放,容易形成瓶颈。
- sync.RWMutex:在读操作远多于写操作的场景下,读写锁能显著提高性能。由于读操作可以并发执行,减少了读操作之间的等待时间。但如果写操作频繁,写操作需要等待所有读操作完成,也可能会导致性能下降。
适用场景
- 适合使用Mutex的场景:当读写操作频率相近,或者写操作比较频繁时,使用Mutex可以简单有效地保证数据一致性。因为读写锁在写操作时需要等待所有读操作完成,在写操作频繁的情况下,这种等待可能会导致性能问题,而Mutex直接限制同一时间只有一个操作,避免了复杂的等待逻辑。
package main import ( "fmt" "sync" ) var ( mu sync.Mutex count int ) func main() { var wg sync.WaitGroup for i := 0; i < 10; i++ { wg.Add(1) go func() { defer wg.Done() mu.Lock() count++ fmt.Println("Incremented count:", count) mu.Unlock() }() } wg.Wait() }
- 适合使用读写锁的场景:当读操作远远多于写操作时,使用读写锁能提高并发性能。读操作可以并行执行,只有写操作需要独占资源。
package main import ( "fmt" "sync" ) var ( rwmu sync.RWMutex count int ) func read(id int, wg *sync.WaitGroup) { defer wg.Done() rwmu.RLock() fmt.Printf("Reader %d read count: %d\n", id, count) rwmu.RUnlock() } func write(id int, wg *sync.WaitGroup) { defer wg.Done() rwmu.Lock() count++ fmt.Printf("Writer %d incremented count to: %d\n", id, count) rwmu.Unlock() } func main() { var wg sync.WaitGroup for i := 0; i < 5; i++ { wg.Add(1) go read(i, &wg) } for i := 0; i < 2; i++ { wg.Add(1) go write(i, &wg) } wg.Wait() }