概述 Go的sync/atomic包提供了原子操作,支持的数据类型包括:
int32, int64, uint32, uint64, uintptr, unsafe.Pointer 1 若需要扩大原子操作的适用范围,可以使用atomic包中的Value。利用它可以实现对任意值进行原子得存储与加载。
使用注意点 atomic.Value只有两个指针方法:Store、Load。使用时需要遵循两个原则:1.不能存储nil;2.存储第一个值后,就只能存储这个类型的值。 看一下atom.Value的实现可以发现一个内部的结构ifaceWords,使用unsafe.Pointer存储数据类型及内容得指针
type ifaceWords struct { typ unsafe.Pointer data unsafe.Pointer } 1 2 3 4 在使用Store进行存储时,首先判断待存储值是否为nil,若为nil会直接panic。之后会读取typ,若为nil则会将待存储值得类型、值的指针分别对typ、data进行赋值;若不为nil,则会判断待存储值的类型是否与既有的typ一致,不一致也会引起panic,一致的话则是将新值的指针赋予data。 为了防止在使用是意外出现panic,所以可以考虑在外部先进行合法性校验。
引用类型带来的坑点 因为atom.Value内部实际上维护的是存储值的指针,而这个指针因为不对外暴露,所以认为是并发安全的。然而如果尝试用它来存储引用类型,维护的就是这个引用类型的指针,则不能保证实际的数据是并发安全的。举个例子: uint32是值类型,切片[]uint32是引用类型,我们使用这样两个函数来尝试修改值。
//值类型 func atomic_value(a uint32) { var v atomic.Value v.Store(a) a = 666 fmt.Println(a) fmt.Println(v.Load()) }
// 引用类型 func atomic_slice(s []uint32) { var v atomic.Value v.Store(s) s[0] = 666 fmt.Println(s) fmt.Println(v.Load()) } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 然后我们分别调用这两个函数
func TestAtomValue(t *testing.T) { atomic_value(1) atomic_slice([]uint32{1,2,3}) } 1 2 3 4 最后它的输出是这样的:
可以看到我们使用Store存入uint32的值a后,我们无论怎么在外部修改a,使用Load都可以获取到我们Store的值。 然而我们若使用Store存入引用类型的切片,我们在外部修改值,Load出来的值也会收到影响。这是因为对于一个引用类型,我们实际上只是Store了一个指针,只是对一个指针的原子操作,而这个指针实际指向的地址的值,并不在atomic.Value的维护下,所以并不是并发安全的。
总结 1.atomic.Value可以实现对自定义类型的原子操作 2.不能存入nil 3.对于同一个atomic.Value不能存入类型不同的值 4.最好不要使用atomic.Value存储引用类型的值,可能导致数据不是并发安全的 ———————————————— 版权声明:本文为CSDN博主「雨儿酱在鹿上」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/q895431756/article/details/111063656
|
请发表评论