龙象建设集团有限公司网站,学做网站论坛会员账号,网络营销公司经营范围,网站信息组织优化文章目录 概要1、基本知识1.1 原子操作是什么1.2 CPU怎么实现原子操作的#xff1f; 2、atomic包2.1、 Add函数2.2、CompareAndSwap函数2.3、Swap函数2.4、Load函数2.5、Store函数 3、atomic.Value值 概要
atomic包是golang通过对底层系统支持的原子操作进行封装#xff0c;… 文章目录 概要1、基本知识1.1 原子操作是什么1.2 CPU怎么实现原子操作的 2、atomic包2.1、 Add函数2.2、CompareAndSwap函数2.3、Swap函数2.4、Load函数2.5、Store函数 3、atomic.Value值 概要
atomic包是golang通过对底层系统支持的原子操作进行封装而提供的原子操作包用于实现无锁化并发安全的操作数据。支持加法Add、比较并交换CompareAndSwap、直接交换Swap、设置指针变量值Store、获取指针变量值Load还设置了atomic.Value结构支持对象存储、获取、比较并交换、直接交换操作。
atmoic包怎么用以加法为例变量加法运算操作并不是原子性的包括从内存中取值放入加法寄存器、寄存器运算得到结果、将结果写回内存在并发环境下一个协程的运算结果可能被另一个覆盖从而导致丢失修改的问题例如以下代码的最后打印结果不会是10000。
var wg sync.WaitGroup
var num 0func Add() {numwg.Done()
}
func main() {size : 10000wg.Add(size)for i : 0; i size; i {go Add()}wg.Wait()fmt.Println(num)
}而以下代码时10000:
var wg sync.WaitGroup
var num int32 0func Add() {atomic.AddInt32(num, 1)wg.Done()
}
func main() {size : 10000wg.Add(size)for i : 0; i size; i {go Add()}wg.Wait()fmt.Println(num)
}1、基本知识
1.1 原子操作是什么
原子操作是指在执行过程中不会被中断的操作要么全部执行完成要么完全不执行不存在部分执行的情况。原子操作可以看作是一个不可分割的单元其他线程或进程无法在其中间进行干扰或插入。 原子操作通常用于对共享数据进行读取、写入和修改等操作以保证数据的一致性和正确性。在多线程或多进程环境下如果多个线程或进程同时访问和修改同一份共享数据没有正确的同步机制或使用原子操作可能会导致数据竞争和不确定的行为。 常见的原子操作包括
原子读Atomic Read从内存中获取一个共享变量的值并确保其他线程或进程不会在读取过程中修改该值。原子写Atomic Write将一个值写入到共享变量并确保其他线程或进程不会在写入过程中读取或修改该值。原子加Atomic Add将一个特定的值与共享变量相加并将结果存回共享变量中确保其他线程或进程不会在加法过程中干扰。原子比较并交换Atomic Compare-and-Swap比较共享变量的当前值与预期值是否相等如果相等则将新值写入共享变量如果不相等则不做任何修改。该操作常用于实现同步机制和锁。
1.2 CPU怎么实现原子操作的
现在的CPU一般为多核处理器它底层实现原子操作的方式有多种下面介绍几种常见的方法 总线锁定在多核处理器中可以使用总线锁定机制来确保原子操作。当一个处理器需要执行原子操作时它会向总线发送一个请求锁定的信号。其他处理器在接收到该信号后将暂停对总线的访问以防止干扰正在进行的原子操作。只有当原子操作完成并释放锁定时其他处理器才能继续对总线进行访问。 缓存一致性协议多核处理器中的每个核心通常都具有自己的缓存这就需要确保各个核心之间的缓存数据的一致性。缓存一致性协议可以通过在多个核心之间共享和更新处理器**缓存行缓存在cpu核心本地缓存中的一条数据**的状态信息来实现原子操作。常见的缓存一致性协议有MESI多个核心更新数据时需要遵循以下原则 Modified修改状态当一个核心修改了一个缓存行中的数据时该缓存行将被标记为“修改”状态。此时其他核心的缓存中对应的缓存行就被视为无效Invalid即无效数据。 Exclusive独占状态当一个核心从内存中加载了一个缓存行到自己的缓存中并且该缓存行在其他核心的缓存中没有副本时该缓存行处于“独占”状态。这时其他核心需要读取或写入该数据时必须通过该核心进行访问独占状态的数据可以直接进行修改。 Shared共享状态当多个核心都拥有同一个缓存行的副本时该缓存行处于“共享”状态。此时对于读操作其他核心可以直接从自己的缓存中读取数据。对于写操作需要通过总线发出命令让其他缓存的状态更新为“失效”该缓存中数据更新为独占才能修改。 Invalid无效状态当一个核心修改了一个缓存行的数据时该缓存行在其他核心的数据状态会被标记为“无效”状态。这意味着其他核心需要重新从内存中加载最新的数据。 总的逻辑是当一个核心读取内存中数据在自己的本地缓存中并且只有该核心的缓存拥有该缓存行这个缓存行的状态为独占状态此时可直接进行读写操作其它缓存读取该缓存行需要通过总线想该核心发送请求进行读取此时缓存行状态变为共享。核心可以读取缓存行数据但修改需要先通过总线发起修改请求将其它核心的该缓存行数据置为无效再修改缓存行中数据看起来效率很低其实cpu会马上修改该缓存行修改完后缓存不会马上生效而是放入store buffer中再反送失效请求给所有核心由store buffer等待全部核心响应成功再生效缓存行的修改这段期间核心可以继续做其它事情。修改完成后缓存行为修改状态其他核心的该缓存行为无效状态此时若有核心读取该缓存行就将缓存行写入内存该核心上的缓存行状态更新为共享若是修改请求就将核心缓存行状态更新为无效。 原子指令现代的多核处理器通常会提供一些原子指令例如比较并交换compare and swap、加载和存储条件load-linked/store-conditional等。这些指令能够以硬件级别的原子方式执行从而保证对共享数据的访问是原子的。通过使用这些原子指令可以避免锁的开销并提供更高效的原子操作。
2、atomic包
Golang的atomic包的原子操作是通过CPU指令实现的。在大多数CPU架构中原子操作的实现都是基于32位或64位的寄存器。Golang的atomic包的原子操作函数会将变量的地址转换为指针型的变量并使用CPU指令对这个指针型的变量进行操作。 Golang的atomic包提供了一组原子操作函数包括Add、CompareAndSwap、Load、Store、Swap等函数。这些函数的具体作用如下
Add函数用于对一个整数型的变量进行加法操作并返回新的值。CompareAndSwap函数用于比较并交换一个指针型的变量的值。如果变量的值等于旧值就将变量的值设置为新值并返回true否则不修改变量的值并返回false。Load函数用于获取一个指针型的变量的值。Store函数用于设置一个指针型的变量的值。Swap函数用于交换一个指针型的变量的值并返回旧值。 让我们更具体地来看一下Golang的atomic包的原子操作
2.1、 Add函数
Add函数用于对一个整数型的变量进行加法操作并返回新的值。Add函数的定义如下
func AddInt32(addr *int32, delta int32) (new int32)
func AddInt64(addr *int64, delta int64) (new int64)
func AddUint32(addr *uint32, delta uint32) (new uint32)
func AddUint64(addr *uint64, delta uint64) (new uint64)
func AddUintptr(addr *uintptr, delta uintptr) (new uintptr)其中addr表示要进行加法操作的变量的地址delta表示要加上的值。Add函数会将变量的值加上delta并返回新的值。
2.2、CompareAndSwap函数
CompareAndSwap函数用于比较并交换一个指针型的变量的值。如果变量的值等于旧值就将变量的值设置为新值并返回true否则不修改变量的值并返回false。CompareAndSwap函数的定义如下
func CompareAndSwapInt32(addr *int32, old, new int32) (swapped bool)
func CompareAndSwapInt64(addr *int64, old, new int64) (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)
func CompareAndSwapPointer(addr *unsafe.Pointer, old, new unsafe.Pointer) (swapped bool)其中addr表示要进行比较和交换的变量的地址old表示旧值new表示新值。如果变量的值等于旧值就将变量的值设置为新值并返回true否则不修改变量的值并返回false。
2.3、Swap函数
Swap函数用于交换一个指针型的变量的值并返回旧值。Swap函数的定义如下
func SwapInt32(addr *int32, new int32) (old int32)
func SwapInt64(addr *int64, new int64) (old int64)
func SwapUint32(addr *uint32, new uint32) (old uint32)
func SwapUint64(addr *uint64, new uint64) (old uint64)
func SwapUintptr(addr *uintptr, new uintptr) (old uintptr)
func SwapPointer(addr *unsafe.Pointer, new unsafe.Pointer) (old unsafe.Pointer)其中addr表示要交换的变量的地址new表示新值。Swap函数会将变量的值设置为new并返回旧值。
2.4、Load函数
Load函数用于获取一个指针型的变量的值。Load函数的定义如下
func LoadInt32(addr *int32) (val int32)
func LoadInt64(addr *int64) (val int64)
func LoadUint32(addr *uint32) (val uint32)
func LoadUint64(addr *uint64) (val uint64)
func LoadUintptr(addr *uintptr) (val uintptr)
func LoadPointer(addr *unsafe.Pointer) (val unsafe.Pointer)2.5、Store函数
Store函数用于设置一个指针型的变量的值。Store函数的定义如下
func StoreInt32(addr *int32, val int32)
func StoreInt64(addr *int64, val int64)
func StoreUint32(addr *uint32, val uint32)
func StoreUint64(addr *uint64, val uint64)
func StoreUintptr(addr *uintptr, val uintptr)
func StorePointer(addr *unsafe.Pointer, val unsafe.Pointer)其中addr表示要设置的变量的地址val表示要设置的值。Store函数会将变量的值设置为val。
3、atomic.Value值
可以用来存储any类型对象的结构体存取操作均是原子操作具体方法如下
type Value struct {v any
}
// 加载Value中存的值
func (v *Value) Load() (val any) {}
// 存储对象放入Value中
func (v *Value) Store(val any) {}
// 交换Value中存的数据
func (v *Value) Swap(new any) (old any) {)
// 比较并存储值必须铜类型且可比较采用使用该方法否则panic传入old、new对象如果old对象等于Value内存储的对象就将新的对象存入返回treu。否则false
func (v *Value) CompareAndSwap(old, new any) (swapped bool) {}实践一下
package mainimport (fmtsync/atomic
)type Config struct {Addr string
}var config atomic.Valuefunc main() {conf1 : Config{Addr: 1.1.1.1,}conf2 : Config{Addr: 2.2.2.2,}config.Store(conf1)oldData : config.Swap(conf2)newData : config.Load()fmt.Println(oldData, newData)conf3 : Config{Addr: 3.3.3.3,}ok : config.CompareAndSwap(conf1, conf3)fmt.Println(ok, config.Load())ok config.CompareAndSwap(conf2, conf3)fmt.Println(ok, config.Load())
}输出
{1.1.1.1} {2.2.2.2}
false {2.2.2.2}
true {3.3.3.3}