因为原子操作不能被中断,所以它需要足够简单,并且要求快速。因此,操作系统层面只对二进制位或整数的原子操作提供了支持。
原子操作只支持有限的数据类型,所以多数场景下,往往互斥锁更合适。
package main
import (
"fmt"
"sync/atomic"
)
func main() {
num := uint32(18)
fmt.Printf("The number: %d\n", num)
// 无符号整数减法方式一
delta := int32(-3)
atomic.AddUint32(&num, uint32(delta))
fmt.Printf("The number: %d\n", num)
// 无符号整数减法方式二,利用补码
atomic.AddUint32(&num, ^uint32(-(-3)-1))
fmt.Printf("The number: %d\n", num)
}
交换:把新值赋给变量,并返回变量的旧值。
比较并交换(CAS):先判断当前被操作变量的值,如果与预期的旧值相等,就把新值赋给该变量,并返回 true 表明交换操作已进行;否则旧忽略交换操作,并返回 false。
package main
import (
"fmt"
"sync/atomic"
"time"
)
func main() {
forAndCAS1()
fmt.Println()
forAndCAS2()
}
// 简易的自旋锁
func forAndCAS1() {
sign := make(chan struct{}, 2)
num := int32(0)
fmt.Printf("The number: %d\n", num)
// 定时增加 num 的值
go func() {
defer func() {
sign <- struct{}{}
}()
for {
time.Sleep(time.Millisecond * 500)
newNum := atomic.AddInt32(&num, 2)
fmt.Printf("The number: %d\n", newNum)
if newNum == 6 {
break
}
}
}()
// 定时检查 num 的值,如果等于 6 就归零
go func() {
defer func() {
sign <- struct{}{}
}()
// 只要条件未被满足,就会阻塞
for {
if atomic.CompareAndSwapInt32(&num, 6, 0) {
fmt.Println("The number has gone to zero.")
break
}
time.Sleep(time.Millisecond * 500)
}
}()
<- sign
<- sign
}
// 一种简易(且更加宽松的)互斥锁的模拟
func forAndCAS2() {
sign := make(chan struct{}, 2)
num := int32(0)
fmt.Printf("The number: %d\n", num)
max := int32(10)
// 定时增加 num 的值
go func(id int, max int32) {
defer func() {
sign <- struct{}{}
}()
for i := 0; ; i++ {
currNum := atomic.LoadInt32(&num)
if currNum >= max {
break
}
newNum := currNum + 2
time.Sleep(time.Millisecond * 200)
if atomic.CompareAndSwapInt32(&num, currNum, newNum) {
fmt.Printf("The number: %d [%d-%d]\n", newNum, id, i)
} else {
fmt.Printf("The CAS operation failed. [%d-%d]\n", id, i)
}
}
}(0, max)
// 定时增加 num 的值
go func(id int, max int32) {
defer func() {
sign <- struct{}{}
}()
for i := 0; ; i++ {
currNum := atomic.LoadInt32(&num)
if currNum >= max {
break
}
newNum := currNum + 2
time.Sleep(time.Millisecond * 200)
if atomic.CompareAndSwapInt32(&num, currNum, newNum) {
fmt.Printf("The number: %d [%d-%d]\n", newNum, id, i)
} else {
fmt.Printf("The CAS operation failed. [%d-%d]\n", id, i)
}
}
}(1, max)
<- sign
<- sign
}
在保证了对一个变量的写操作是原子操作后,进行读操作的时候同样需要原子操作,是为了防止读操作读到没有被修改完的值。
为了扩大原子操作的适用范围,Go 在 1.4 版本中向 sync/atomic 包中添加了一个新类型 Value。
此类型的值相当于一个容器,可被“原子地”存储和加载任意的值。
atomic.Value 类型是开箱即用的,声明后即可适用。
它只有两个指针方法:Store 和 Load。
尽量不要向原子值中存储引用类型的值,容易造成安全漏洞。
package main
import (
"fmt"
"sync/atomic"
"io"
"errors"
"os"
"reflect"
)
func main() {
fmt.Printf("1\n")
var box atomic.Value
fmt.Println("Copy box to box2.")
box2 := box // 原子值在真正使用前可以被复制
v1 := [...]int{1, 2, 3}
fmt.Printf("Store %v to box.\n", v1)
box.Store(v1)
fmt.Printf("The value load from box is %v.\n", box.Load())
fmt.Printf("The value load from box2 is %v.\n", box2.Load())
fmt.Println()
fmt.Printf("2\n")
v2 := "123"
fmt.Printf("Store %q to box2.\n", v2)
box2.Store(v2) // 不会引发 panic
fmt.Printf("The value load from box is %v.\n", box.Load())
fmt.Printf("The value load from box2 is %q.\n", box2.Load())
fmt.Println()
fmt.Printf("3\n")
fmt.Println("Copy box to box3.")
box3 := box // 原子值在真正使用后不应该被复制
fmt.Printf("The value load from box3 is %v.\n", box3.Load())
v3 := 123
fmt.Printf("Store %d to box3.\n", v3)
// box3.Store(v3) // 引发 panic,存储值的类型不一致
_ = box3
fmt.Println()
fmt.Printf("4\n")
var box4 atomic.Value
v4 := errors.New("something wrong")
fmt.Printf("Store an error with message %q to box4.\n", v4)
box4.Store(v4)
v41 := io.EOF
fmt.Println("Store a value of the same type to box4.")
box4.Store(v41)
v42, ok := interface{}(&os.PathError{}).(error)
if ok {
fmt.Printf("Store a value of type %T that implements error interface to box4.\n", v42)
// box4.Store(v42) // 引发 panic,存储值类型不一致
}
fmt.Println()
fmt.Printf("5\n")
box5, err := NewAtomicValue(v4)
if err != nil {
fmt.Printf("error: %s\n", err)
}
fmt.Printf("The legal type in box5 is %s.\n", box5.TypeOfValue())
fmt.Println("Store a value of the same type to box5.")
err = box5.Store(v41)
if err != nil {
fmt.Printf("error:%s\n", err)
}
fmt.Printf("Store a value of type %T that implements error interface to box5.\n", v42)
err = box5.Store(v42)
if err != nil {
fmt.Printf("error: %s\n", err)
}
fmt.Println()
fmt.Printf("6\n")
var box6 atomic.Value
v6 := []int{1, 2, 3}
fmt.Printf("Store %v to box6.\n", v6)
box6.Store(v6)
v6[1] = 4 // 不是并发安全的
fmt.Printf("The value load from box6 is %v.\n", box6.Load())
// 正确的做法:
v6 = []int{1, 2, 3}
store := func(v []int) {
replica := make([]int, len(v))
copy(replica, v)
box6.Store(replica)
}
fmt.Printf("Store %v to box6.\n", v6)
store(v6)
v6[2] = 5 // 安全的,原值涉及的数据与副本无关
fmt.Printf("The value load from box6 is %v.\n", box6.Load())
}
// 将原子值装入一个结构体中,更安全
type atomicValue struct {
v atomic.Value
t reflect.Type
}
func NewAtomicValue(example interface{}) (*atomicValue, error) {
if example == nil {
return nil, errors.New("atomic value: nil example")
}
return &atomicValue {
t: reflect.TypeOf(example),
}, nil
}
func (av *atomicValue) Store (v interface{}) error {
if v == nil {
return errors.New("atomic value: nil value")
}
t := reflect.TypeOf(v)
if t != av.t { // 先判断被存储值类型的合法性
return fmt.Errorf("atomic value: wrong type: %s", t)
}
av.v.Store(v)
return nil
}
func (av *atomicValue) Load() interface{} {
return av.v.Load()
}
func (av *atomicValue) TypeOfValue() reflect.Type {
return av.t
}
版权声明:本文为u012319493原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。