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 的,光是调度器花去的开销就站了大头。

更新时间: