最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

深度剖析golang中的sync包

 更新時(shí)間:2025年12月18日 10:38:25   作者:Clarence Liu  
go語(yǔ)言的sync包提供了多種并發(fā)控制工具,如Mutex、RWMutex、WaitGroup、Once、Cond和Map等,用于保護(hù)共享資源和協(xié)調(diào)多個(gè)goroutine的執(zhí)行,這些工具的原理和使用方法在文中都有詳細(xì)介紹,感興趣的朋友跟隨小編一起看看吧

簡(jiǎn)介

  • golang的sync包提供了一些并發(fā)控制的工具,在應(yīng)用程序開(kāi)發(fā)過(guò)程中是非常有用的,下面詳細(xì)介紹下這些工具的原理和使用
  • 在介紹工具之前,先講解下內(nèi)存模型中的happens-before關(guān)系
// 示例:Happens-Before關(guān)系
var a string
var done bool
func setup() {
    a = "hello, world"  // 寫操作A
    done = true         // 寫操作B
}
func main() {
    go setup()
    for !done {        // 讀操作C
        // 忙等待
    }
    print(a)           // 讀操作D
}

關(guān)鍵點(diǎn)

  • 如果B happens-before C,那么A happens-before D
  • sync包的作用就是建立這種happens-before關(guān)系

Mutex

  • golang提供的鎖,常用于保護(hù)共享資源的訪問(wèn)安全,使用如下
func main() {
	var mu sync.Mutex
	mu.Lock()
	defer mu.Unlock()
	mu.TryLock()
}
  • mu.Lock():加鎖
  • mu.Unlock():解鎖
  • mu.TryLock():嘗試加鎖,如果成功會(huì)返回true

鎖結(jié)構(gòu)如下

type Mutex struct {
    state int32  // 鎖狀態(tài):包含多個(gè)標(biāo)志位
    sema  uint32 // 信號(hào)量:用于阻塞goroutine
}
  • 加鎖的時(shí)候,會(huì)先使用cas原子操作獲取鎖(CPU實(shí)現(xiàn)),如果拿不到說(shuō)明已經(jīng)被其他goroutine占有,通過(guò)自旋阻塞當(dāng)前goroutine(這過(guò)程會(huì)通過(guò)PAUSE指令減少CPU功耗)
func (m *Mutex) Lock() {
    // 快速路徑:嘗試原子操作獲取鎖
    if atomic.CompareAndSwapInt32(&m.state, 0, mutexLocked) {
        return
    }
    // 慢速路徑:鎖已被持有,需要等待
    m.lockSlow()
}
  • 讀寫均衡或者寫者較多的場(chǎng)景,使用這個(gè)鎖(防止寫者競(jìng)爭(zhēng),內(nèi)存占用也更小)

RWMutex

  • golang提供的讀寫鎖,和上面的鎖區(qū)別是區(qū)分了讀和寫兩種場(chǎng)景。讀之間的沖突不阻塞,若寫鎖遇到了讀鎖,需要等待所有讀者釋放,若讀者和寫者同時(shí)到達(dá),讀者要等待寫者完成(寫優(yōu)先)
func (rw *RWMutex) Lock() {
    // 1. 獲取互斥鎖
    rw.w.Lock()
    // 2. 設(shè)置readerCount為負(fù)值,阻止新讀者
    r := atomic.AddInt32(&rw.readerCount, -rwmutexMaxReaders)
    // 3. 等待現(xiàn)有讀者
    if r != 0 {
        runtime_Semacquire(&rw.writerSem)
    }
}
func (rw *RWMutex) RLock() {
    // 檢查是否有寫者(readerCount < 0表示有寫者)
    if atomic.AddInt32(&rw.readerCount, 1) < 0 {
        // 有寫者在等待或正在寫,讀者必須阻塞
        runtime_Semacquire(&rw.readerSem)
    }
}
  • 讀多寫少的場(chǎng)景使用這個(gè)讀寫鎖,性能會(huì)有比較大的提升

WaitGroup

  • waitGroup提供了下面三個(gè)方法,用來(lái)控制多個(gè)goroutine是否都執(zhí)行完成,主goroutine調(diào)用Add方法設(shè)置要等待的goroutine數(shù)量,每個(gè)goroutine結(jié)束的時(shí)候,調(diào)用Done方法標(biāo)記這個(gè)任務(wù)結(jié)束,Wait方法會(huì)等待所有g(shù)oroutine調(diào)用完Done方法
func main() {
	var wg sync.WaitGroup
	wg.Add(1) // 增加一個(gè)等待者
	go func() {
		defer wg.Done()
		// 執(zhí)行邏輯, 邏輯執(zhí)行完之后, 等待者數(shù)量-1
	}()
	wg.Wait() // 等待所有等待者執(zhí)行完成
}
  • 底層實(shí)現(xiàn)如下,它使用了一個(gè)state來(lái)存儲(chǔ)等待者和要執(zhí)行的工作數(shù)量
type WaitGroup struct {
    noCopy noCopy
    // 64位值的高32位是計(jì)數(shù)器,低32位是等待者數(shù)量
    // 64位原子操作需要64位對(duì)齊,但32位編譯器不能確保這一點(diǎn)
    state atomic.Uint64 // high 32 bits are counter, low 32 bits are waiter count.
}
// WaitGroup狀態(tài)布局(64位)
// 高32位:計(jì)數(shù)器 (counter)
// 低32位:等待者數(shù)量 (waiters)
func (wg *WaitGroup) Add(delta int) {
    statep, semap := wg.state()
    // 更新計(jì)數(shù)器
    state := atomic.AddUint64(statep, uint64(delta)<<32)
    // 檢查狀態(tài)變化
    v := int32(state >> 32)  // 計(jì)數(shù)器
    w := uint32(state)       // 等待者數(shù)量
    if v > 0 || w == 0 {
        return // 還有工作要做,或者沒(méi)有等待者
    }
    // 所有工作完成,喚醒等待者
    if *statep != 0 {
        panic("sync: WaitGroup misuse")
    }
    // 喚醒所有等待的goroutine
    for ; w != 0; w-- {
        runtime_Semrelease(semap, false, 0)
    }
}

具體

  1. 高32位 = 工作計(jì)數(shù)器
    wg.Add(5) // 高32位 += 5,表示還有5個(gè)工作要做
    wg.Done() // 高32位 -= 1,表示完成了1個(gè)工作
  • 當(dāng)高32位變?yōu)?時(shí),表示所有工作完成
  1. 低32位 = 等待者數(shù)量
    wg.Wait() // 低32位 += 1,表示1個(gè)goroutine開(kāi)始等待
  • 當(dāng)高32位變?yōu)?時(shí),低32位表示需要喚醒多少個(gè)goroutine

當(dāng)調(diào)用Wait方法時(shí),也是一個(gè)自旋等待的邏輯。配合cas,實(shí)現(xiàn)等待所有工作完成。具體細(xì)節(jié)參考源碼,這里只簡(jiǎn)單介紹下原理

Once

  • sync.Once{}提供了一個(gè)多次調(diào)用,只執(zhí)行一次的方法實(shí)現(xiàn),如下所示
func main() {
	once := sync.Once{}
	once.Do(func() {
		// 這里的邏輯只會(huì)執(zhí)行一次
	})
}
  • 這個(gè)的底層實(shí)現(xiàn)比較簡(jiǎn)單,因?yàn)閛nce內(nèi)部維護(hù)了一個(gè)原子類,指向一個(gè)32位無(wú)符號(hào)數(shù),當(dāng)執(zhí)行func的時(shí)候,如果有多個(gè)goroutine并發(fā)執(zhí)行,兩個(gè)goroutine會(huì)競(jìng)爭(zhēng)鎖,最終只有一個(gè)能夠執(zhí)行func的邏輯,執(zhí)行完之后,這個(gè)數(shù)會(huì)設(shè)置為1,從而后面的所有g(shù)oroutine都不會(huì)再走到執(zhí)行邏輯。具體參考下面源碼實(shí)現(xiàn)
func (o *Once) Do(f func()) {
	if o.done.Load() == 0 {
		o.doSlow(f)
	}
}
func (o *Once) doSlow(f func()) {
	o.m.Lock()
	defer o.m.Unlock()
	if o.done.Load() == 0 {
		defer o.done.Store(1)
		f()
	}
}

Cond

  • 這個(gè)工具主要是協(xié)調(diào)訪問(wèn)共享資源的那些goroutine,c.L是工具內(nèi)部的一個(gè)鎖。當(dāng)調(diào)用c.L.Wait()會(huì)解鎖等待Signal或者Broadcast信號(hào),Broadcast會(huì)喚醒所有等待的Wait,Signal會(huì)隨機(jī)喚醒一個(gè)等待的Wait。具體使用方法如下面的例子所示
func main() {
	cond := sync.NewCond(new(sync.Mutex))
	done := false
	read := func(name string, c *sync.Cond) {
		c.L.Lock()
		if !done {
			c.Wait()
		}
		fmt.Println("start reading: ", name)
		c.L.Unlock()
	}
	write := func(name string, c *sync.Cond) {
		fmt.Println("start writing: ", name)
		c.L.Lock()
		done = true
		c.L.Unlock()
		fmt.Println("wakes all:", name)
		c.Signal()
	}
	go read("read3", cond)
	go read("read1", cond)
	go read("read2", cond)
	write("write1", cond)
	time.Sleep(5 * time.Second)
}

Map

  • golang在sync包內(nèi)提供了一個(gè)并發(fā)安全的Map,可以增刪改查,遍歷、compareAndSwap、LoadOrStore等操作??梢宰鲆恍o(wú)鎖編程,提升性能
  • 讀多寫少(讀操作占比>80%)、數(shù)據(jù)量大(鍵值對(duì)數(shù)量>1000)、寫入不頻繁(寫入頻率<100次/秒)、簡(jiǎn)單鍵值等場(chǎng)景,使用這個(gè)的性能要優(yōu)于普通map+鎖

Pool

  • 對(duì)于頻繁進(jìn)行內(nèi)存分配和回收的場(chǎng)景,可以使用sync.Pool{},例如下面的場(chǎng)景
// 頻繁分配和回收的典型特征
func frequentAllocation() {
    pool := sync.Pool{
        New: func() interface{} {
            return make([]byte, 1024)
        },
    }
    // 在循環(huán)中重復(fù)創(chuàng)建和銷毀對(duì)象
    for i := 0; i < 1000; i++ {
        // 分配:創(chuàng)建新對(duì)象
        // buf := make([]byte, 1024)  // 直接分配內(nèi)存
        buf := pool.Get().([]byte) // 使用對(duì)象池
        // 使用對(duì)象
        copy(buf, data[i])
        process(buf)
        // 回收:對(duì)象超出作用域,被GC回收
        pool.Put(buf[:0]) // 歸還對(duì)象(清空)
    }
    // 每次循環(huán)都經(jīng)歷:分配→使用→回收
}

到此這篇關(guān)于深度剖析golang中的sync包的文章就介紹到這了,更多相關(guān)goland sync包內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

您可能感興趣的文章:

相關(guān)文章

  • Go項(xiàng)目的目錄結(jié)構(gòu)詳解

    Go項(xiàng)目的目錄結(jié)構(gòu)詳解

    這篇文章主要介紹了Go項(xiàng)目的目錄結(jié)構(gòu),對(duì)基礎(chǔ)目錄做了講解,對(duì)項(xiàng)目開(kāi)發(fā)中的其它目錄也一并做了介紹,需要的朋友可以參考下
    2014-10-10
  • Golang 端口復(fù)用測(cè)試的實(shí)現(xiàn)

    Golang 端口復(fù)用測(cè)試的實(shí)現(xiàn)

    這篇文章主要介紹了Golang 端口復(fù)用測(cè)試的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-03-03
  • Linux環(huán)境下編譯并運(yùn)行g(shù)o項(xiàng)目的全過(guò)程

    Linux環(huán)境下編譯并運(yùn)行g(shù)o項(xiàng)目的全過(guò)程

    Go語(yǔ)言是Google的開(kāi)源編程語(yǔ)言,廣泛應(yīng)用于云計(jì)算、分布式系統(tǒng)開(kāi)發(fā)等領(lǐng)域,在Linux上也有大量的應(yīng)用場(chǎng)景,這篇文章主要給大家介紹了關(guān)于Linux環(huán)境下編譯并運(yùn)行g(shù)o項(xiàng)目的相關(guān)資料,需要的朋友可以參考下
    2023-11-11
  • Golang中文件目錄操作的實(shí)現(xiàn)步驟詳解

    Golang中文件目錄操作的實(shí)現(xiàn)步驟詳解

    在Golang中,文件目錄是指計(jì)算機(jī)文件系統(tǒng)中的文件夾或目錄。目錄是用于組織和存儲(chǔ)文件的一種方式,可以包含文件和其他子目錄,本文主要介紹了Golang中文件目錄操作的實(shí)現(xiàn)方法,需要的朋友可以參考下
    2023-05-05
  • 在 Golang 中實(shí)現(xiàn) Cache::remember 方法詳解

    在 Golang 中實(shí)現(xiàn) Cache::remember 方法詳解

    這篇文章主要介紹了在 Golang 中實(shí)現(xiàn) Cache::remember 方法詳解,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-03-03
  • Go json反序列化“null“的問(wèn)題解決

    Go json反序列化“null“的問(wèn)題解決

    本文主要介紹了Go json反序列化“null“的問(wèn)題解決,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-03-03
  • Go語(yǔ)言切片(Slice)使用技巧與避坑指南

    Go語(yǔ)言切片(Slice)使用技巧與避坑指南

    切片(Slice)是Go語(yǔ)言中最靈活且高頻使用的數(shù)據(jù)結(jié)構(gòu)之一,其本質(zhì)是對(duì)底層數(shù)組的動(dòng)態(tài)引用視圖,支持動(dòng)態(tài)擴(kuò)容、高效截取等特性,本文將結(jié)合代碼示例,詳細(xì)解析切片的核心用法及常見(jiàn)注意事項(xiàng),需要的朋友可以參考下
    2025-06-06
  • Golang實(shí)現(xiàn)讀取ZIP壓縮包并顯示Gin靜態(tài)html網(wǎng)站

    Golang實(shí)現(xiàn)讀取ZIP壓縮包并顯示Gin靜態(tài)html網(wǎng)站

    這篇文章主要為大家詳細(xì)介紹了如何通過(guò)Golang實(shí)現(xiàn)從ZIP壓縮包讀取內(nèi)容并作為Gin靜態(tài)網(wǎng)站顯示,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2025-07-07
  • 手把手帶你走進(jìn)Go語(yǔ)言之常量解析

    手把手帶你走進(jìn)Go語(yǔ)言之常量解析

    這篇文章主要介紹了Go語(yǔ)言之常量解析,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-09-09
  • go進(jìn)行http請(qǐng)求偶發(fā)EOF問(wèn)題分析

    go進(jìn)行http請(qǐng)求偶發(fā)EOF問(wèn)題分析

    go使用連接池進(jìn)行http請(qǐng)求,一般都能請(qǐng)求成功,但偶然會(huì)出現(xiàn)請(qǐng)求失敗返回EOF錯(cuò)誤的情況,本文主要來(lái)帶大家分析一下為什么會(huì)出現(xiàn)這樣的問(wèn)題并提供解決方法,需要的可以參考下
    2025-01-01

最新評(píng)論

永清县| 宿迁市| 通江县| 忻城县| 富民县| 红河县| 德庆县| 八宿县| 泉州市| 金华市| 永胜县| 宾川县| 绩溪县| 永泰县| 翁源县| 广丰县| 无为县| 长乐市| 勐海县| 贡嘎县| 逊克县| 塔城市| 精河县| 德令哈市| 盘锦市| 布尔津县| 武清区| 景谷| 桐柏县| 乐业县| 乌鲁木齐县| 铁岭县| 昭通市| 淮安市| 仁寿县| 赤峰市| 客服| 上思县| 北碚区| 合川市| 长顺县|