@
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) |
| } |
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) |
| } |
结果输出
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) { |
| |
| |
| 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 |
| } |

【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
· 上周热点回顾(2.17-2.23)