golang并发编程-05-同步-03-原子操作(atomic包)

@

Go语言提供的原子操作都是非侵入式的。它们由标准库代码包sync/atomic中的众多函数代表。

1 载入

1.1 Load 的6个函数

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)

1.2 使用示例

import (
"fmt"
"sync/atomic"
)
func main() {
var a int32
a = 5
b := atomic.LoadInt32(&a)
fmt.Println(b)
}

1.3 *unsafe.Pointer

优势:unsafe.Pointer 是一个万能指针,可以在任何指针类型之间做转化
弊端:绕过了 Go 的类型安全机制,所以是不安全的操作。

func main() {
i := 10
var p *int = &i
var fp *float32 = (*float32)(unsafe.Pointer(p))
*fp = *fp * 10
fmt.Println(i) // 100
}

2. 存储

2.1 Store的6个函数

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)

2.2 使用示例

为了方便说明,我们将a分为值aInt 和指针aPi两个变量。(当然你像上例中表示指针也没有问题)

func main() {
var aInt int64
fmt.Println("a 的初始值: ",aInt)
aPi := &aInt
atomic.StoreInt64(aPi,66)
fmt.Println("存入新值后,a的值:",aInt)
}

结果输出

a 的初始值: 0
存入新值后,a的值: 66

3. 增/减

3.1 Add的5个函数

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)

3.2 使用示例(五丈原七星灯续命——原子加/减)

说明:五丈原,诸葛亮阳寿将尽。七星灯续命12年,魏延踢到七星灯。

func main() {
fmt.Println("======== 进入五丈原 ========")
var remainingAge int32
remainingAge = 0
fmt.Printf("【武侯】生命余额:%d 年\n",remainingAge)
atomic.AddInt32(&remainingAge, +12)
fmt.Printf("+++++七星灯续命成功++++\n【武侯】生命余额:%d 年\n",remainingAge)
atomic.AddInt32(&remainingAge, -12)
fmt.Printf("-----魏延踢翻七星灯----\n【武侯】生命余额:%d 年\n======= END 将星陨落 =====",remainingAge)
}

输出

======== 进入五丈原 ========
【武侯】生命余额:0
+++++七星灯续命成功++++
【武侯】生命余额:12
-----魏延踢翻七星灯----
【武侯】生命余额:0
======= END 将星陨落 =====

4. 比较并交换

4.1 Swap的5个函数

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)

4.2 示例 (孙策之死——原子交换值)

说明:妖人于吉施法交换了孙策和于吉的剩余生命

func main() {
var yuJiReAge int64 = 0
var sunCeReAge int64 = 20
fmt.Printf("【于吉】生命余额 %d 年\n【孙策】生命余额:%d 年\n",yuJiReAge,sunCeReAge)
fmt.Println("====== 妖人施法了 ======")
sunCeReAge = atomic.SwapInt64(&yuJiReAge,sunCeReAge)
fmt.Printf("【于吉】生命余额 %d 年\n【孙策】生命余额:%d 年\n",yuJiReAge,sunCeReAge)
}

输出

【于吉】生命余额 0
【孙策】生命余额:20
====== 妖人施法了 ======
【于吉】生命余额 20
【孙策】生命余额:0

5. 比较并交换

  • 作用
    比较old值,当old值为N的时候交换成新值。

5.1 CompareAndSwap的6个函数

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)

5.2 示例(妖人于吉——原子比较,伺机交换)

说明:妖人于吉法术提升:等待自己生命值降为零的时候,再获得孙策的阳寿。(注意,因为此函数不会再返回old值,因此他不能直接干掉孙策)

func main() {
var sunCeReAge int64 = 30
var yuJiReAge int64 = 3
var i int64
for i=1;i<100;i++{
sunCeReAge -= 1
yuJiReAge -= 1
fmt.Printf("===== 第%d年====\n【于吉】生命余额 %d 年\n【孙策】生命余额:%d 年\n",i,yuJiReAge,sunCeReAge)
time.Sleep(time.Second)
if atomic.CompareAndSwapInt64(&yuJiReAge, 0, sunCeReAge) {
fmt.Printf("+++++ 妖人法术被触发 +++++\n【于吉】生命余额 %d 年\n【孙策】生命余额:%d 年\n",yuJiReAge,sunCeReAge)
break
}
}
}

输出

===== 第1年====
【于吉】生命余额 2
【孙策】生命余额:29
===== 第2年====
【于吉】生命余额 1
【孙策】生命余额:28
===== 第3年====
【于吉】生命余额 0
【孙策】生命余额:27
+++++ 妖人法术被触发 +++++
【于吉】生命余额 27
【孙策】生命余额:27

6. 一个并发的练习

gitlab获取库有很多页(假设我们不知道有多少个项目页不知道有多少页)
如果一个协程访问会很慢
设计一个函数获取gitlab所有项目数据。

func GetAllProjectsInfo() (results []GitlabProject,err error) {
var page int32
var lock sync.Mutex
var wg sync.WaitGroup
wg.Add(20)
for i := 0; i < 20; i++ {
tmp := i
go func(tmp int) {
//fmt.Printf("go routine : %d starts\n",tmp)
for {
newPage := atomic.AddInt32(&page, 1)
r, err := GetProjectsInfo(int(newPage))
if err !=nil {
log.Printf("线程 %d 获取 第 %d 页 数据失败,错误:%+v\n",tmp,newPage,err)
continue
}
if len(r) == 0 {
break
}else {
lock.Lock()
results = append(results, r...)
lock.Unlock()
}
}
fmt.Printf("go routine : %d ends\n",tmp)
wg.Done()
}(tmp)
}
wg.Wait()
fmt.Printf("result : %d \n",len(results))
return
}

posted on   运维开发玄德公  阅读(19)  评论(0编辑  收藏  举报  

相关博文:
阅读排行:
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
· 上周热点回顾(2.17-2.23)
< 2025年2月 >
26 27 28 29 30 31 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 1
2 3 4 5 6 7 8

导航

统计

点击右上角即可分享
微信分享提示