原文链接:戳这里
前言
Goi++iGosync/atomicGo
什么是原子性、原子操作
原子(atomic)本意是”不能被进一步宰割的最小粒子”,而原子操作(atomic operation)意为”不可中断的一个或一系列操作”。其实用大白话说进去就是让多个线程对同一块内存的操作是串行的,不会因为并发操作把内存写的不合乎预期。咱们来看这样一个例子:
假如当初是一个银行账户零碎,用户A想要本人从本人的账户直达1万元到用户B的账户上,直到转帐胜利实现一个事务,次要做这两件事:
- 从A的账户中减去1万元,如果A的账户原来就有2万元,当初就变成了1万元
- 给B的账户增加1万元,如果B的账户原来有2万元,那么当初就变成了3万元
假如在操作一的时候,零碎产生了故障,导致给B账户增加款项失败了,那么就要进行回滚。回滚就是回到事务之前的状态,咱们把这种要么一起胜利的操作叫做原子操作,而原子性就是要么残缺的被执行、要么齐全不执行。
如何保障原子性
- 锁机制
在处理器层面,能够采纳总线加锁或者对缓存加锁的形式来实现多处理器之间的原子操作。通过加锁保障从零碎内存中读取或写入一个字节是原子的,也就是当一个处理器读取一个字节时,其余处理器不能拜访这个字节的内存地址。
Lock#CPULock#Lock#
锁机制尽管能够保障原子性,然而锁机制会存在以下问题:
- 多线程竞争的状况下,频繁的加锁、开释锁会导致较多的上下文切换和调度延时,性能会很差
- 当一个线程占用工夫比拟长时,就多导致其余须要此锁的线程挂起.
下面咱们说的都是乐观锁,要解决这种低效的问题,咱们能够采纳乐观锁,每次不加锁,而是假如没有抵触去实现某项操作,如果因为抵触失败就重试,直到胜利为止。也就是咱们接下来要说的CAS(compare and swap).
- CAS(compare and swap)
Compare And SwapCPUintelCPUcmpxchgCAS
VENVEVNVECASV
伪代码能够这样写:
func CompareAndSwap(int *addr,int oldValue,int newValue) bool{
if *addr == nil{
return false
}
if *addr == oldValue {
*addr = newValue
return true
}
return false
}
ABA
go语言中如何进行原子操作
Gosync/atomicGo
func SwapXXXX(addr *int32, new int32) (old int32)new*addr
old = *addr
*addr = new
return old
func CompareAndSwapXXXX((addr *int64, old, new int64) (swapped bool)*addrnew*addr
if *addr == old{
*addr = new
return ture
}
return false
func AddXXXX(addr *int64, delta int64) (new int64)val*addr
*addr += delta
return *addr
func LoadXXXX(addr *uint32) (val uint32)*addrfunc StoreXXXX(addr *int32, val int32)*addr
Go1.4Value
源码解析
ValuePlan9plan9
Value
Value
type Value struct {
v interface{}
}
ValueinterfaceStorepanicinterfaceifaceWordsinterfaceinterface
// ifaceWords is interface{} internal representation.
type ifaceWords struct {
typ unsafe.Pointer
data unsafe.Pointer
}
Value
咱们一起来看一看他是如何实现写入操作的:
// Store sets the value of the Value to x.
// All calls to Store for a given Value must use values of the same concrete type.
// Store of an inconsistent type panics, as does Store(nil).
func (v *Value) Store(x interface{}) {
if x == nil {
panic("sync/atomic: store of nil value into Value")
}
vp := (*ifaceWords)(unsafe.Pointer(v))
xp := (*ifaceWords)(unsafe.Pointer(&x))
for {
typ := LoadPointer(&vp.typ)
if typ == nil {
// Attempt to start first store.
// Disable preemption so that other goroutines can use
// active spin wait to wait for completion; and so that
// GC does not see the fake type accidentally.
runtime_procPin()
if !CompareAndSwapPointer(&vp.typ, nil, unsafe.Pointer(^uintptr(0))) {
runtime_procUnpin()
continue
}
// Complete first store.
StorePointer(&vp.data, xp.data)
StorePointer(&vp.typ, xp.typ)
runtime_procUnpin()
return
}
if uintptr(typ) == ^uintptr(0) {
// First store in progress. Wait.
// Since we disable preemption around the first store,
// we can wait with active spinning.
continue
}
// First store completed. Check type and overwrite data.
if typ != xp.typ {
panic("sync/atomic: store of inconsistently typed value into Value")
}
StorePointer(&vp.data, xp.data)
return
}
}
// Disable/enable preemption, implemented in runtime.
func runtime_procPin()
func runtime_procUnpin()
Store
nilpanicunsafe.PointeroldValuenewValueifaceWordsforStoreruntime_procPin()GCCASunsafe.Pointer(^uintptr(0))vuintptr(typ) == ^uintptr(0)panic
这里代码量没有多少,置信大家肯定看懂了吧~。
Value
先看一下代码:
// Load returns the value set by the most recent Store.
// It returns nil if there has been no call to Store for this Value.
func (v *Value) Load() (x interface{}) {
vp := (*ifaceWords)(unsafe.Pointer(v))
typ := LoadPointer(&vp.typ)
if typ == nil || uintptr(typ) == ^uintptr(0) {
// First store not yet completed.
return nil
}
data := LoadPointer(&vp.data)
xp := (*ifaceWords)(unsafe.Pointer(&x))
xp.typ = typ
xp.data = data
return
}
unsafe.PointeroldValueifaceWords
LoadPointerinterfacetypdata
小彩蛋
ABAdemoGoatomic.CompareAndSwapXXX
func main() {
var share uint64 = 1
wg := sync.WaitGroup{}
wg.Add(3)
// 协程1,期望值是1,欲更新的值是2
go func() {
defer wg.Done()
swapped := atomic.CompareAndSwapUint64(&share,1,2)
fmt.Println("goroutine 1",swapped)
}()
// 协程2,期望值是1,欲更新的值是2
go func() {
defer wg.Done()
time.Sleep(5 * time.Millisecond)
swapped := atomic.CompareAndSwapUint64(&share,1,2)
fmt.Println("goroutine 2",swapped)
}()
// 协程3,期望值是2,欲更新的值是1
go func() {
defer wg.Done()
time.Sleep(1 * time.Millisecond)
swapped := atomic.CompareAndSwapUint64(&share,2,1)
fmt.Println("goroutine 3",swapped)
}()
wg.Wait()
fmt.Println("main exit")
}
总结
sync.once
好啦,这篇文章就到这里啦,素质三连(分享、点赞、在看)都是笔者继续创作更多优质内容的能源!
创立了一个Golang学习交换群,欢送各位大佬们踊跃入群,咱们一起学习交换。入群形式:加我vx拉你入群,或者公众号获取入群二维码
结尾给大家发一个小福利吧,最近我在看[微服务架构设计模式]这一本书,讲的很好,本人也收集了一本PDF,有须要的小伙能够到自行下载。获取形式:关注公众号:[Golang梦工厂],后盾回复:[微服务],即可获取。
我翻译了一份GIN中文文档,会定期进行保护,有须要的小伙伴后盾回复[gin]即可下载。
翻译了一份Machinery中文文档,会定期进行保护,有须要的小伙伴们后盾回复[machinery]即可获取。
我是asong,一名普普通通的程序猿,让咱们一起缓缓变强吧。欢送各位的关注,咱们下期见~~~
举荐往期文章:
- machinery-go异步工作队列
- 详解defer实现机制
- 真的了解interface了嘛
- Leaf—Segment分布式ID生成零碎(Golang实现版本)
- 十张动图带你搞懂排序算法(附go实现代码)
- go参数传递类型
- 手把手教姐姐写音讯队列
- 常见面试题之缓存雪崩、缓存穿透、缓存击穿
- 详解Context包,看这一篇就够了!!!
- go-ElasticSearch入门看这一篇就够了(一)
- 面试官:go中for-range应用过吗?这几个问题你能解释一下起因吗