ContextGo

文章的主要话题如下:

Go

原子操作

原子操作即是进行过程中不能被中断的操作,针对某个值的原子操作在被进行的过程中,CPU绝不会再去进行其他的针对该值的操作。为了实现这样的严谨性,原子操作仅会由一个独立的CPU指令代表和完成。原子操作是无锁的,常常直接通过CPU指令直接实现。 事实上,其它同步技术的实现常常依赖于原子操作。

Go对原子操作的支持

sync/atomic
  • Go语言提供的原子操作都是非入侵式的。
  • 这些函数提供的原子操作共有五种:增减、比较并交换、载入、存储、交换。
  • 原子操作支持的类型类型包括int32、int64、uint32、uint64、uintptr、unsafe.Pointer。
AddInt32main goroutinegoroutinegoroutine
package main

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

func main() {
    var n int32
	  var wg sync.WaitGroup
	  for i := 0; i < 1000; i++ {
		    wg.Add(1)
		    go func() {
			      atomic.AddInt32(&n, 1)
			      wg.Done()
		    }()
	  }
	  wg.Wait()

    fmt.Println(atomic.LoadInt32(&n)) // output:1000
}
复制代码
atomic.AddInt32(&n, 1)ngoroutine
CASsync/atomicCompareAndSwap
func CompareAndSwapInt32(addr *int32, old, new int32) (swapped bool)
func CompareAndSwapPointer(addr *unsafe.Pointer,old, new unsafe.Pointer) (swapped bool)
......
复制代码
CompareAndSwapaddroldtruenew
mutexCASCAS
package main

import (
    "fmt"
    "sync/atomic"
)

var value int32 = 1

func main()  {
    fmt.Println("======old value=======")
    fmt.Println(value)
    addValue(10)
    fmt.Println("======New value=======")
    fmt.Println(value)

}

//不断地尝试原子地更新value的值,直到操作成功为止
func addValue(delta int32){
    for {
        v := value
        if atomic.CompareAndSwapInt32(&value, v, (v + delta)){
            break
        }
    }
}
复制代码
v:= valuevvaluesync/atomicLoad

竞争条件是由于异步的访问共享资源,并试图同时读写该资源而导致的,使用互斥锁和通道的思路都是在线程获得到访问权后阻塞其他线程对共享内存的访问,而使用原子操作解决数据竞争问题则是利用了其不可被打断的特性。

atomic

原子操作与互斥锁的区别

Goatomicsync
atomicCAS
CASCASCAS

所以总结下来原子操作与互斥锁的区别有:

  • 互斥锁是一种数据结构,用来让一个线程执行程序的关键部分,完成互斥的多个操作。
  • 原子操作是针对某个值的单个互斥操作。
  • 可以把互斥锁理解为悲观锁,共享资源每次只给一个线程使用,其它线程阻塞,用完后再把资源转让给其它线程。
atomicsync
atomic
sync/atomic

推荐阅读