当前位置:网站首页>《7天學會Go並發編程》第7天 go語言並發編程Atomic原子實戰操作含ABA問題
《7天學會Go並發編程》第7天 go語言並發編程Atomic原子實戰操作含ABA問題
2022-06-27 22:23:00 【大錘愛編程】
為什麼在最後一篇文章才會講解Atmoic操作,是因為atomic操作在業務開發中應用較少,故將其放在最後。但是論重要性來說,atomic操作是最核心最關鍵一環。
系列文章目錄
《7天學會Go並發編程》第一天 go並發實戰初窺門徑_大錘愛編程的博客-CSDN博客
《7天學會Go並發編程》の第二天 寫一個簡單的Go並發程序以及Goroutine的使用_大錘愛編程的博客-CSDN博客
《7天學會Go並發編程》第三天 go的Mutex使用_大錘愛編程的博客-CSDN博客
《7天學會Go並發編程》第四天 sync.Once和sync.WaitGroup學習應用實踐_大錘愛編程的博客-CSDN博客
《7天學會Go並發編程》第五天 go語言Context的應用和實現 go實現超時判斷_大錘愛編程的博客-CSDN博客
《7天學會Go並發編程》第六天 go語言Sync.cond的應用和實現 go實現多線程聯合執行_大錘愛編程的博客-CSDN博客
《7天學會Go並發編程》第7天 go語言並發編程Atomic原子實戰操作含ABA問題_大錘愛編程的博客-CSDN博客
目錄
一、Atomic-原子操作簡介
原子操作其實和原子沒有什麼關系,主要借用的是原子不可分割的概念來强調說明這個操作也不可以被分割。
使用並發編程時需要解决的就是臨界區(公共資源的修改)問題,抽象的數據操作行為是RAW,WAR,WAW。🥤🥤🥤體現在具體編碼過程中,就是RW,在RW過程中,不會存在其他線程對這個臨界區就是操作。就是這個原子操作一次只可以被一個線程來完成,不存在多個線程同時進行原子操作。
簡單的概括就是:
使用原子操作時,就像過一個兩頭窄,中間寬的胡同。兩頭窄到只允許一個線程通過。
這樣:

二、go語言Atomic源碼分析和實戰操作
go語言Atmoic相關定義實現在主要在sync.Atomic包中,Atmoic包中提供低級原子內存原語
用於實現同步算法。
2.1 Atomic 關鍵函數及其釋義
doc.go主要定義了SWAP(變量之間的交換操作)、CAS(變量比較交換操作)、ADD(原子變量加操作)、LOAD(原子變量的數據加載操作)、STORE(原子變量的存儲操作)。
其中相對來說比較複雜的就是CAS操作:
比較並交換(compare and swap, CAS),用於在多線程編程中實現不被打斷的數據交換操作,從而避免多線程同時改寫某一數據時由於執行順序不確定性以及中斷的不可預知性產生的數據不一致問題。 該操作通過將內存中的值與指定數據進行比較,當數值一樣時將內存中的數據替換為新的值。
源碼釋義:
舊值和內存中的地址進行比較,如果舊值和內存地址比較相等的說,則說明內存地址並未修改,可以使用新值寫入地址。
入參定義:
addr *uint64:內存存儲數據的地址
old:舊值
new uint64: 新值
返參定義:
swapped (TRUE:交換成功,FALSE:交換失敗)
// CompareAndSwapUint64 executes the compare-and-swap operation for a uint64 value.
func CompareAndSwapUint64(addr *uint64, old, new uint64) (swapped bool)CAS上的ABA問題:
ABA問題是CAS常見的一種問題,可基本錶述為:
- 進程P1讀取了一個數值A
- P1被掛起(時間片耗盡、中斷等),進程P2開始執行
- P2修改數值A為數值B,然後又修改回A
- P1被喚醒,比較後發現數值A沒有變化,程序繼續執行
邏輯上CAS是是可以解决多線程數據交換的沖突問題,但是如果內存中存儲的值被其他線程修改過了,且修改的值和原有舊值一樣。其具體危害詳見https://zh.wikipedia.org/zh-cn/%E6%AF%94%E8%BE%83%E5%B9%B6%E4%BA%A4%E6%8D%A2
https://zh.wikipedia.org/zh-cn/%E6%AF%94%E8%BE%83%E5%B9%B6%E4%BA%A4%E6%8D%A2
完整源碼:
package atomic
import (
"unsafe"
)
// BUG(rsc): On 386, the 64-bit functions use instructions unavailable before the Pentium MMX.
//
// On non-Linux ARM, the 64-bit functions use instructions unavailable before the ARMv6k core.
//
// On ARM, 386, and 32-bit MIPS, it is the caller's responsibility
// to arrange for 64-bit alignment of 64-bit words accessed atomically.
// The first word in a variable or in an allocated struct, array, or slice can
// be relied upon to be 64-bit aligned.
// SwapInt32 atomically stores new into *addr and returns the previous *addr value.
func SwapInt32(addr *int32, new int32) (old int32)
// SwapInt64 atomically stores new into *addr and returns the previous *addr value.
func SwapInt64(addr *int64, new int64) (old int64)
// SwapUint32 atomically stores new into *addr and returns the previous *addr value.
func SwapUint32(addr *uint32, new uint32) (old uint32)
// SwapUint64 atomically stores new into *addr and returns the previous *addr value.
func SwapUint64(addr *uint64, new uint64) (old uint64)
// SwapUintptr atomically stores new into *addr and returns the previous *addr value.
func SwapUintptr(addr *uintptr, new uintptr) (old uintptr)
// SwapPointer atomically stores new into *addr and returns the previous *addr value.
func SwapPointer(addr *unsafe.Pointer, new unsafe.Pointer) (old unsafe.Pointer)
// CompareAndSwapInt32 executes the compare-and-swap operation for an int32 value.
func CompareAndSwapInt32(addr *int32, old, new int32) (swapped bool)
// CompareAndSwapInt64 executes the compare-and-swap operation for an int64 value.
func CompareAndSwapInt64(addr *int64, old, new int64) (swapped bool)
// CompareAndSwapUint32 executes the compare-and-swap operation for a uint32 value.
func CompareAndSwapUint32(addr *uint32, old, new uint32) (swapped bool)
// CompareAndSwapUint64 executes the compare-and-swap operation for a uint64 value.
func CompareAndSwapUint64(addr *uint64, old, new uint64) (swapped bool)
// CompareAndSwapUintptr executes the compare-and-swap operation for a uintptr value.
func CompareAndSwapUintptr(addr *uintptr, old, new uintptr) (swapped bool)
// CompareAndSwapPointer executes the compare-and-swap operation for a unsafe.Pointer value.
func CompareAndSwapPointer(addr *unsafe.Pointer, old, new unsafe.Pointer) (swapped bool)
// AddInt32 atomically adds delta to *addr and returns the new value.
func AddInt32(addr *int32, delta int32) (new int32)
// AddUint32 atomically adds delta to *addr and returns the new value.
// To subtract a signed positive constant value c from x, do AddUint32(&x, ^uint32(c-1)).
// In particular, to decrement x, do AddUint32(&x, ^uint32(0)).
func AddUint32(addr *uint32, delta uint32) (new uint32)
// AddInt64 atomically adds delta to *addr and returns the new value.
func AddInt64(addr *int64, delta int64) (new int64)
// AddUint64 atomically adds delta to *addr and returns the new value.
// To subtract a signed positive constant value c from x, do AddUint64(&x, ^uint64(c-1)).
// In particular, to decrement x, do AddUint64(&x, ^uint64(0)).
func AddUint64(addr *uint64, delta uint64) (new uint64)
// AddUintptr atomically adds delta to *addr and returns the new value.
func AddUintptr(addr *uintptr, delta uintptr) (new uintptr)
// LoadInt32 atomically loads *addr.
func LoadInt32(addr *int32) (val int32)
// LoadInt64 atomically loads *addr.
func LoadInt64(addr *int64) (val int64)
// LoadUint32 atomically loads *addr.
func LoadUint32(addr *uint32) (val uint32)
// LoadUint64 atomically loads *addr.
func LoadUint64(addr *uint64) (val uint64)
// LoadUintptr atomically loads *addr.
func LoadUintptr(addr *uintptr) (val uintptr)
// LoadPointer atomically loads *addr.
func LoadPointer(addr *unsafe.Pointer) (val unsafe.Pointer)
// StoreInt32 atomically stores val into *addr.
func StoreInt32(addr *int32, val int32)
// StoreInt64 atomically stores val into *addr.
func StoreInt64(addr *int64, val int64)
// StoreUint32 atomically stores val into *addr.
func StoreUint32(addr *uint32, val uint32)
// StoreUint64 atomically stores val into *addr.
func StoreUint64(addr *uint64, val uint64)
// StoreUintptr atomically stores val into *addr.
func StoreUintptr(addr *uintptr, val uintptr)
// StorePointer atomically stores val into *addr.
func StorePointer(addr *unsafe.Pointer, val unsafe.Pointer)

2.1 atomic實戰
atomic基本使用來說比較簡單,就是使用atmoc.func來完成對應的增加,賦值操作。
package main
import (
"fmt"
"sync/atomic"
)
func main() {
var operationNums = int32(10)
atomic.AddInt32(&operationNums,12)
fmt.Printf("修改後的數值:%d\n",atomic.LoadInt32(&operationNums))
}
2.2 atomic原子並發操作
釋義:並發修改變量的值,並發閱讀變量的值。
package main
import (
"fmt"
"sync"
"sync/atomic"
"time"
)
func main() {
var operationNums = int32(10)
group := sync.WaitGroup{}
group.Add(20)
for i := 0; i < 10; i++ {
go func() {
defer group.Done()
time.Sleep(200*time.Millisecond)
atomic.AddInt32(&operationNums,1)
}()
}
for i := 0; i < 10; i++ {
go func(i int) {
defer group.Done()
time.Sleep(200*time.Millisecond)
fmt.Printf("線程%d閱讀修改後的數值:%d\n",i,atomic.LoadInt32(&operationNums))
}(i)
}
group.Wait()
}
2.3 任意數據結構的原子操作
通過var聲明一個atomic.Value類型的變量。對這個變量進行相關的原子操作。
package main
import (
"fmt"
"sync"
"sync/atomic"
"time"
)
func main() {
var box atomic.Value
fmt.Println("Copy box to box2.")
v1 := [...]int{1, 2, 3}
fmt.Printf("Store %v to box.\n", v1)
box.Store(v1)
fmt.Printf("The value load from box is %v.\n", box.Load())
fmt.Println()
}執行結果:毫無意外,又是正確執行。
Copy box to box2.
Store [1 2 3] to box.
The value load from box is [1 2 3].三、總結
今天主要介紹了atomic的概念和簡單使用,比較淺顯。對於atomic應用感興趣的同學,可以在go語言相關的並發源碼包中探索。源碼之下,了無秘密。加油加油!️~
🧧🧧🧧感謝諸比特大佬的閱讀,點個關注,收藏一下唄~
![]()
边栏推荐
- 6G显卡显存不足出现CUDA Error:out of memory解决办法
- 石子合并问题分析
- Example of using gbase 8A OLAP function group by grouping sets
- Fill in the blank of rich text test
- [LeetCode]动态规划解拆分整数I[Silver Fox]
- Hash table - sum of arrays
- 登录凭证(cookie+session和Token令牌)
- CDH集群之YARN性能调优
- go语言切片Slice和数组Array对比panic: runtime error: index out of range问题解决
- YOLOv6:又快又准的目标检测框架开源啦
猜你喜欢

. Net learning notes (V) -- lambda, LINQ, anonymous class (VaR), extension method

Exclusive interview with millions of annual salary. What should developers do if they don't fix bugs?
Conversion between flat array and JSON tree

管理系统-ITclub(上)

深度学习又有新坑了!悉尼大学提出全新跨模态任务,用文本指导图像进行抠图

Test birds with an annual salary of 50w+ are using this: JMeter script development -- extension function

Experience sharing of meituan 20K Software Test Engineers

Remote invocation of microservices

The "business and Application Security Development Forum" held by the ICT Institute was re recognized for the security capability of Tianyi cloud

Interval DP of Changyou dynamic programming
随机推荐
Go from introduction to actual combat - execute only once (note)
crontab定时任务常用命令
Figure countdownlatch and cyclicbarrier based on AQS queue
使用sqlite3语句后出现省略号 ... 的解决方法
Gbase 8A OLAP analysis function cume_ Example of dist
Dynamic refresh mapper
CDH集群之YARN性能调优
Open source technology exchange - Introduction to Chengying, a one-stop fully automated operation and maintenance manager
软件测试自动化测试之——接口测试从入门到精通,每天学习一点点
Gao fushuai in the unit testing industry, pytest framework, hands-on teaching, will do this in the future test reports~
清华大学教授:软件测试已经走入一个误区——“非代码不可”
信通院举办“业务与应用安全发展论坛” 天翼云安全能力再获认可
[LeetCode]动态规划解分割数组II[Arctic Fox]
Beijing University of Posts and Telecommunications - multi-agent deep reinforcement learning for cost and delay sensitive virtual network function placement and routing
[LeetCode]100. Same tree
正则表达式
Educational Codeforces Round 108 (Rated for Div. 2)
Gbase 8A OLAP analysis function cume_ Example of dist
Introduction to ARCS Model
扁平数组和JSON树的转换
