亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Go語(yǔ)言原子操作atomic的使用

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

概述

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

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

同步問(wèn)題

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

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ā)安全,但是加鎖性能開(kāi)銷(xiāo)大
		go atomicAdd() // 原子操作版add函數(shù),并發(fā)安全,性能優(yōu)于加鎖版
	}
	wg.Wait()
	end := time.Now()
	fmt.Println("計(jì)算結(jié)果:", x)
	fmt.Println("消耗時(shí)間:", end.Sub(start))
}

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

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

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

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

從上面的結(jié)果可以看出,普通版本的直接結(jié)算結(jié)果就是錯(cuò)誤的,這個(gè)因?yàn)槠胀ò姹镜牟皇遣l(fā)安全的,所以會(huì)導(dǎo)致計(jì)算錯(cuò)誤。加鎖版本和原子操作版本都是計(jì)算正確,但是原子操作版本所消耗時(shí)間要比加鎖版本更低(如果數(shù)字更大相差時(shí)間可能會(huì)更多,可以自行嘗試)。

atomic

所有的原子操作都在atomic包下面,對(duì)于int32,int64,uint32,uint64,uintptr和Pointer類(lèi)型,都有其對(duì)應(yīng)的原子操作。

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種類(lèi)型,下面根據(jù)不同類(lèi)型逐一講解。

Load和Store

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

var value int64

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

Add

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

Swap和CompareAndSwap

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

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

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設(shè)置為1,先使用Swap方法將Value修改為2,同時(shí)返回修改前的值。再使用CompareAndSwap想要修改為3,但是因?yàn)閭魅氲钠谕?和value的實(shí)際值2不相等,所以修改失敗,再次調(diào)用期望值為2且value的實(shí)際值為2,則修改成功。運(yùn)行結(jié)果如下:

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

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

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

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())
}

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

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

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

相關(guān)文章

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

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

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

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

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

    GoLang中生成UUID唯一標(biāo)識(shí)的實(shí)現(xiàn)方法

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

    Go語(yǔ)言 go程釋放操作(退出/銷(xiāo)毀)

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

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

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

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

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

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

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

    go語(yǔ)言import報(bào)錯(cuò)處理圖文詳解

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

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

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

    Go中的條件語(yǔ)句Switch示例詳解

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

最新評(píng)論