atomic vs lock
atomic & lock
计算2000次累加
- 起 2 个 Goroutine ,用 atomic 累加;
- 起 2 个 Goroutine ,加写锁;
- 起 1 个 Goroutine ,用 atomic 累加;
- 起 1 个 Goroutine ,加写锁;
- 起 1 个 Goroutine ,不加锁;
const N = 1000
// 起 2 个 Goroutine ,用 atomic 累加;
func Atomic() {
var count int64 = 0
wg := sync.WaitGroup{}
wg.Add(2)
go func() {
for i := 1; i <= N; i++ {
atomic.AddInt64(&count, 1)
}
wg.Done()
}()
go func() {
for i := 1; i <= N; i++ {
atomic.AddInt64(&count, 1)
}
wg.Done()
}()
wg.Wait()
}
// 起 2 个 Goroutine ,加写锁;
func Lock() {
var count int64 = 0
mu := sync.Mutex{}
wg := sync.WaitGroup{}
wg.Add(2)
go func() {
for i := 1; i <= N; i++ {
mu.Lock()
count++
mu.Unlock()
}
wg.Done()
}()
go func() {
for i := 1; i <= N; i++ {
mu.Lock()
count++
mu.Unlock()
}
wg.Done()
}()
wg.Wait()
}
func Atomic1() {
var count int64 = 0
n := 2 * N
for i := 1; i <= n; i++ {
atomic.AddInt64(&count, 1)
}
}
// 单 Goroutine 自然是没必要加锁,主要是测试加锁,释放的开销
func Lock1() {
var count int64 = 0
mu := sync.Mutex{}
n := 2 * N
for i := 1; i <= n; i++ {
mu.Lock()
count++
mu.Unlock()
}
}
// 无锁
func WithoutLock() {
var count int64 = 0
n := 2 * N
for i := 1; i <= n; i++ {
count++
}
}
执行 go test -v -bench . -benchmem
BenchmarkAtomic-4 73255 16685 ns/op 24 B/op 2 allocs/op
BenchmarkLock-4 36656 32512 ns/op 32 B/op 3 allocs/op
BenchmarkAtomic1-4 113552 10412 ns/op 8 B/op 1 allocs/op
BenchmarkLock1-4 48069 24808 ns/op 8 B/op 1 allocs/op
BenchmarkWithoutLock-4 2032491 592 ns/op 0 B/op 0 allocs/op
显然代码越简单,性能越好,无锁操作性能远好于其他操作。atomic 操作性能也好于 Lock 操作。
当然这个测试不够客观。对于多个 Goroutine 的,光是调度器花去的开销就站了大头。