返回

巧用 Go 原子操作,用好锁,告别性能低迷!##

后端

开篇并发是业务开发中经常要面对的问题,很多时候我们会直接用一把 sync.Mutex 互斥锁来线性化处理,保证每一时刻进入临界区的 goroutine 只有一个。这样避免了并发,但性能也随着降低。

什么是原子操作

原子操作是指不可分割的操作 。在执行原子操作时,不会被其他 goroutine 中断,这意味着原子操作是线程安全的。

Go 中的原子操作

Go 中提供了原子操作包 sync/atomic,它提供了几个内置的原子操作函数,可以用于对共享变量进行原子操作。

AddInt64

AddInt64 函数将一个 int64 类型的值添加到一个共享的 int64 类型变量中。

package main

import (
	"fmt"
	"sync/atomic"
)

func main() {
	var count int64 = 0

	// 使用原子操作将 count 增加 1000 次
	for i := 0; i < 1000; i++ {
		atomic.AddInt64(&count, 1)
	}

	fmt.Println("最终计数:", count)
}

CompareAndSwapInt64

CompareAndSwapInt64 函数将一个共享的 int64 类型变量的值与一个给定的值进行比较,如果相等,则将该变量的值更新为另一个给定的值。

package main

import (
	"fmt"
	"sync/atomic"
)

func main() {
	var count int64 = 0

	// 尝试将 count 的值从 0 更新为 1
	success := atomic.CompareAndSwapInt64(&count, 0, 1)

	if success {
		fmt.Println("更新成功!")
	} else {
		fmt.Println("更新失败!")
	}

	fmt.Println("最终计数:", count)
}

atomic.Value

atomic.Value 类型提供了一种原子操作,可以存储和修改共享变量。

package main

import (
	"fmt"
	"sync"
	"sync/atomic"
)

func main() {
	// 创建一个 atomic.Value 并设置初始值
	var count atomic.Value
	count.Store(0)

	// 使用原子操作将 count 增加 1000 次
	var wg sync.WaitGroup
	for i := 0; i < 1000; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()

			// 将 count 的值增加 1
			count.Store(count.Load().(int) + 1)
		}()
	}

	wg.Wait()

	// 获取最终计数
	finalCount := count.Load()

	fmt.Println("最终计数:", finalCount)
}

结语

Go 中的原子操作是一种非常强大的工具,可以帮助我们提升并发程序的性能。在需要对共享变量进行并发操作时,我们应该优先考虑使用原子操作。