欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Go語言原子操作atomic的使用

 更新時間:2024年10月21日 10:17:25   作者:CodeJR  
本文介紹了Go語言原子操作的使用方法,原子操作是一種無鎖的技術,可通過CPU指令實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧

概述

在日常開發(fā)中,不可避免的會碰到并發(fā)場景,在Go語言中處理同步的方法通常是使用鎖,但如果是對單一的一個整數(shù)操作,這個時候使用鎖可能會造成更大的性能開銷,而且代碼也失去了美觀與優(yōu)雅。

這個時候我們可以使用Go語言自帶的原子操作,原子操作在Go語言的sync/atomic標準庫里面,原子操作是比其他同步技術更基礎的一種技術,而且原子操作是無鎖的,通常是直接通過CPU指令實現(xiàn)。如果去看其他同步技術的源碼可以看到很多技術都是依賴于原子操作的。

同步問題

在正式介紹原子操作之前先看一段代碼,該代碼中創(chuàng)建了100000個協(xié)程,對一個公共變量x進行累加操作,總共有3個版本的代碼,第一個版本是普通版,第二個版本是加鎖版本,第三個版本是原子操作版本。

var (
	x    int64
	lock sync.Mutex
	wg   sync.WaitGroup
)

// 普通版
func add() {
	x++
	wg.Done()
}

// 互斥鎖版
func mutexAdd() {
	lock.Lock()
	x++
	lock.Unlock()
	wg.Done()
}

// atomic版
func atomicAdd() {
	atomic.AddInt64(&x, 1)
	wg.Done()
}

func main() {
	start := time.Now()
	for i := 0; i < 100000; i++ {
		wg.Add(1)
		//go add() // 普通版add函數(shù),非并發(fā)安全
		//go mutexAdd() // 加鎖版add函數(shù),并發(fā)安全,但是加鎖性能開銷大
		go atomicAdd() // 原子操作版add函數(shù),并發(fā)安全,性能優(yōu)于加鎖版
	}
	wg.Wait()
	end := time.Now()
	fmt.Println("計算結(jié)果:", x)
	fmt.Println("消耗時間:", end.Sub(start))
}

依次運行三個版本,得出結(jié)果如下:

# 普通版本
計算結(jié)果: 96725
消耗時間: 26.4237ms

# 加鎖版本
計算結(jié)果: 100000
消耗時間: 31.2588ms

# 原子操作版本
計算結(jié)果: 100000
消耗時間: 27.3615ms

從上面的結(jié)果可以看出,普通版本的直接結(jié)算結(jié)果就是錯誤的,這個因為普通版本的不是并發(fā)安全的,所以會導致計算錯誤。加鎖版本和原子操作版本都是計算正確,但是原子操作版本所消耗時間要比加鎖版本更低(如果數(shù)字更大相差時間可能會更多,可以自行嘗試)。

atomic

所有的原子操作都在atomic包下面,對于int32,int64,uint32,uint64,uintptr和Pointer類型,都有其對應的原子操作。

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)

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)

func AddInt32(addr *int32, delta int32) (new int32)
func AddUint32(addr *uint32, delta uint32) (new uint32)
func AddInt64(addr *int64, delta int64) (new int64)
func AddUint64(addr *uint64, delta uint64) (new uint64)
func AddUintptr(addr *uintptr, delta uintptr) (new uintptr)

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)

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)

以上是atomic包中的所有方法,主要分為5種類型,下面根據(jù)不同類型逐一講解。

Load和Store

Load和Store方法主要用來在并發(fā)環(huán)境下實現(xiàn)對數(shù)字的設置和讀取,Store表示給變量設置一個值,Load表示讀取變量的值。

var value int64

func main() {
	atomic.StoreInt64(&value, 1)
	val := atomic.LoadInt64(&value)
	fmt.Println("value: ", val)
}

Add

Add方法更簡單,就是給一個變量加上一個值,使用Add方法加是并發(fā)安全的,不會出現(xiàn)上面示例中普通版本的add函數(shù)一樣出現(xiàn)計算錯誤的問題。如果變量是有符號整數(shù)類型,需要實現(xiàn)對變量的減法,只需要調(diào)用Add方法的時候第二個參數(shù)傳入負數(shù)即可。

Swap和CompareAndSwap

Swap是交換的意思,使用Swap方法可以修改變量的值,同時會將變量的舊值返回。

CompareAndSwap是比較并交換的意思,作用與Swap類似,也是修改變量的值,但是在調(diào)用CompareAndSwap的時候需要傳入需要設置的新值和期望的舊值,如果當前變量的值和期望的舊值一樣,才會將變量修改會新值,同時返回是否修改成功。

var value int64 = 1

func main() {
	old := atomic.SwapInt64(&value, 2)
	fmt.Printf("舊值:%d, value:%d\n", old, value)
	swapped := atomic.CompareAndSwapInt64(&value, 1, 3)
	fmt.Printf("修改結(jié)果:%t, value: %d\n", swapped, value)
	swapped = atomic.CompareAndSwapInt64(&value, 2, 3)
	fmt.Printf("修改結(jié)果:%t, value: %d\n", swapped, value)
}

上面的示例中將value設置為1,先使用Swap方法將Value修改為2,同時返回修改前的值。再使用CompareAndSwap想要修改為3,但是因為傳入的期望值1和value的實際值2不相等,所以修改失敗,再次調(diào)用期望值為2且value的實際值為2,則修改成功。運行結(jié)果如下:

舊值:1, value:2
修改結(jié)果:false, value: 2
修改結(jié)果:true, value: 3

新版本結(jié)構(gòu)體類型

在1.19版本,Go語言在atomic中新增了Int32,Int64等結(jié)構(gòu)體類型,使用結(jié)構(gòu)體類型進行原子操作更簡單,不需要再想之前一樣每次從atomic中調(diào)用各種類型的方法來實現(xiàn)原子操作。而是只需要使用結(jié)構(gòu)體的方法即可直接進行原子操作。

var value atomic.Int64

func main() {
	value.Store(1)
	fmt.Println("value: ", value.Load())
	n := value.Add(1)
	fmt.Println("value: ", n)
	old := value.Swap(3)
	fmt.Printf("舊值:%d, value:%d\n", old, value.Load())
	swapped := value.CompareAndSwap(3, 4)
	fmt.Printf("修改結(jié)果:%t, value:%d\n", swapped, value.Load())
}

上面示例中使用的就是最新的寫法結(jié)構(gòu)體類型,運行結(jié)果如下:

value:  1
value:  2               
舊值:2, value:3       
修改結(jié)果:true, value:4

到此這篇關于Go語言原子操作atomic的使用的文章就介紹到這了,更多相關Go語言原子操作atomic內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • 淺談一下前端http與https有什么區(qū)別

    淺談一下前端http與https有什么區(qū)別

    這篇文章主要介紹了淺談一下前端http與https有什么區(qū)別,現(xiàn)今大部分的網(wǎng)站都已經(jīng)使用了 https 協(xié)議,那么https對比http協(xié)議有哪些不同呢,需要的朋友可以參考下
    2023-04-04
  • Golang實現(xiàn)密碼加密的示例詳解

    Golang實現(xiàn)密碼加密的示例詳解

    數(shù)據(jù)庫在存儲密碼時,不能明文存儲,需要加密后存儲,而Golang中的加密算法有很多種,下面小編就來通過簡單的示例和大家簡單聊聊吧
    2023-07-07
  • GoLang中生成UUID唯一標識的實現(xiàn)方法

    GoLang中生成UUID唯一標識的實現(xiàn)方法

    UUID是讓分散式系統(tǒng)中的所有元素,都能有唯一的辨識信息,本文主要介紹了GoLang中生成UUID唯一標識的實現(xiàn)方法,具有一定的參考價值,感興趣的可以了解一下
    2024-08-08
  • Go語言 go程釋放操作(退出/銷毀)

    Go語言 go程釋放操作(退出/銷毀)

    這篇文章主要介紹了Go語言 go程釋放操作(退出/銷毀),具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-04-04
  • go?goquery網(wǎng)頁解析實現(xiàn)示例

    go?goquery網(wǎng)頁解析實現(xiàn)示例

    這篇文章主要為大家介紹了go?goquery網(wǎng)頁解析實現(xiàn)示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-08-08
  • Go代碼的組織和格式化規(guī)則實戰(zhàn)示例

    Go代碼的組織和格式化規(guī)則實戰(zhàn)示例

    這篇文章主要為大家介紹了Go代碼的組織和格式化示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-08-08
  • 使用Go語言實現(xiàn)Yaml編碼和解碼的方法詳解

    使用Go語言實現(xiàn)Yaml編碼和解碼的方法詳解

    在這篇文章中,我們將介紹如何使用Go語言編寫代碼來實現(xiàn)Yaml編碼和解碼,文中有詳細的代碼示例供大家參考,對大家的學習和工作有一定的幫助,需要的朋友可以參考下
    2023-11-11
  • go語言import報錯處理圖文詳解

    go語言import報錯處理圖文詳解

    今天本來想嘗試一下go語言中公有和私有的方法,結(jié)果import其他包的時候直接報錯了,下面這篇文章主要給大家介紹了關于go語言import報錯處理的相關資料,需要的朋友可以參考下
    2023-04-04
  • Golang中channel的原理解讀(推薦)

    Golang中channel的原理解讀(推薦)

    channel主要是為了實現(xiàn)go的并發(fā)特性,用于并發(fā)通信的,也就是在不同的協(xié)程單元goroutine之間同步通信。接下來通過本文給大家介紹Golang中channel的原理解讀,感興趣的朋友一起看看吧
    2021-10-10
  • Go中的條件語句Switch示例詳解

    Go中的條件語句Switch示例詳解

    Go的switch的基本功能和C、Java類似,switch 語句用于基于不同條件執(zhí)行不同動作,每一個 case 分支都是唯一的,從上至下逐一測試,直到匹配為止,對Go條件語句Switch相關知識感興趣的朋友一起看看吧
    2021-08-08

最新評論