面试题答案
一键面试原理区别
- atomic包:通过底层硬件指令实现原子操作,直接对内存进行操作,无需操作系统的调度介入,保证操作不可中断,像
atomic.AddInt64
等函数,以硬件级别原子性修改变量。 - 互斥锁(sync.Mutex):基于操作系统的线程调度机制,通过锁定和解锁操作来保护共享资源。当一个goroutine获取锁时,其他goroutine必须等待锁的释放,通过阻塞线程实现数据保护。
性能区别
- atomic包:性能较高,因为是基于硬件指令,无上下文切换开销。适用于简单数据类型(如整型、指针等)的原子操作,在高并发场景下,若只涉及简单数据修改,使用atomic包可避免锁竞争带来的性能损耗。
- 互斥锁(sync.Mutex):性能相对较低,由于涉及操作系统线程调度,获取和释放锁时会产生上下文切换开销。若频繁获取和释放锁,在高并发下会成为性能瓶颈。
适用场景区别
- atomic包:适用于简单数据类型的原子操作场景,如计数器、标志位等。当操作简单且对性能要求极高,不需要复杂同步逻辑时使用。
- 互斥锁(sync.Mutex):适用于复杂数据结构或复杂同步逻辑场景,如对结构体、链表等复杂数据类型的读写保护,或在需要控制多个操作之间顺序的场景中使用。
优先选择atomic包的示例
package main
import (
"fmt"
"sync"
"sync/atomic"
)
func main() {
var counter int64
var wg sync.WaitGroup
numGoroutines := 1000
for i := 0; i < numGoroutines; i++ {
wg.Add(1)
go func() {
defer wg.Done()
atomic.AddInt64(&counter, 1)
}()
}
wg.Wait()
fmt.Println("Final counter value:", counter)
}
在上述示例中,只是对一个简单的计数器进行累加操作,使用atomic.AddInt64
能高效实现,无需使用互斥锁带来的额外开销。如果使用互斥锁,代码会变得复杂且性能不如atomic包,这里就优先选择atomic包。