Golang CAS算法原理与应用


Golang中的CAS算法(Compare And Swap)

CAS(Compare And Swap,比较并交换)是一种无锁编程中常用的原子操作,Golang通过sync/atomic包提供了对CAS的支持。

CAS基本概念

CAS操作包含三个操作数:

  1. 内存位置(V)
  2. 预期原值(A)
  3. 新值(B)

CAS操作逻辑:

如果内存位置V的值等于预期原值A,则将位置V的值修改为新值B,否则不做任何操作

Golang中的CAS实现

在Go中,主要使用atomic.CompareAndSwap系列函数:

func CompareAndSwapInt32(addr *int32, old, new int32) (swapped bool)
func CompareAndSwapInt64(addr *int64, old, new int64) (swapped bool)
func CompareAndSwapPointer(addr *unsafe.Pointer, old, new unsafe.Pointer) (swapped bool)
func CompareAndSwapUint32(addr *uint32, old, new uint32) (swapped bool)
func CompareAndSwapUint64(addr *uint64, old, new uint64) (swapped bool)
func CompareAndSwapUintptr(addr *uintptr, old, new uintptr) (swapped bool)

使用示例

基本使用

package main

import (
	"fmt"
	"sync/atomic"
)

func main() {
	var value int32 = 10
	
	// 尝试将value从10改为20
	swapped := atomic.CompareAndSwapInt32(&value, 10, 20)
	fmt.Println("Swapped:", swapped, "Value:", value) // Swapped: true Value: 20
	
	// 再次尝试将value从10改为30(会失败,因为当前值已经是20)
	swapped = atomic.CompareAndSwapInt32(&value, 10, 30)
	fmt.Println("Swapped:", swapped, "Value:", value) // Swapped: false Value: 20
}

实现自旋锁

package main

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

type SpinLock struct {
	flag int32
}

func (sl *SpinLock) Lock() {
	for !atomic.CompareAndSwapInt32(&sl.flag, 0, 1) {
		// 自旋等待
		time.Sleep(time.Nanosecond)
	}
}

func (sl *SpinLock) Unlock() {
	atomic.StoreInt32(&sl.flag, 0)
}

func main() {
	var lock SpinLock
	var counter int
	
	for i := 0; i < 100; i++ {
		go func() {
			lock.Lock()
			counter++
			lock.Unlock()
		}()
	}
	
	time.Sleep(time.Second)
	fmt.Println("Counter:", counter) // 应该输出100
}

CAS的优缺点

优点

  1. 无锁操作,避免线程阻塞和上下文切换
  2. 适用于竞争不激烈的场景,性能较高

缺点

  1. ABA问题:值可能从A变为B又变回A,CAS会误认为没有变化
  2. 自旋时间长会消耗CPU资源
  3. 只能保证一个共享变量的原子操作

解决ABA问题

Go中可以使用atomic.Value或版本号等方式解决ABA问题:

type ABA struct {
	value int32
	version int32
}

func (a *ABA) Update(newValue int32) {
	for {
		oldValue := atomic.LoadInt32(&a.value)
		oldVersion := atomic.LoadInt32(&a.version)
		if atomic.CompareAndSwapInt32(&a.value, oldValue, newValue) {
			atomic.CompareAndSwapInt32(&a.version, oldVersion, oldVersion+1)
			break
		}
	}
}

总结

CAS是Go中实现无锁并发编程的重要工具,合理使用可以提升程序性能,但也需要注意其局限性和潜在问题。

posted @ 2025-06-25 09:22  guanyubo  阅读(87)  评论(0)    收藏  举报