go---原子操作

因为原子操作不能被中断,所以它需要足够简单,并且要求快速。因此,操作系统层面只对二进制位或整数的原子操作提供了支持。

原子操作只支持有限的数据类型,所以多数场景下,往往互斥锁更合适。

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
}

在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值