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

初識(shí)Golang?Mutex互斥鎖的使用

 更新時(shí)間:2022年10月08日 15:40:44   作者:漫漫Coding路  
在學(xué)習(xí)操作系統(tǒng)的時(shí)候,我們應(yīng)該都學(xué)習(xí)過(guò)臨界區(qū)、互斥鎖這些概念,用于在并發(fā)環(huán)境下保證狀態(tài)的正確性。在?Go語(yǔ)言?里面互斥鎖是?sync.Mutex?,我們本篇文章就來(lái)學(xué)習(xí)下為什么要使用互斥鎖、如何使用互斥鎖,以及使用時(shí)的常見(jiàn)問(wèn)題

前言

在學(xué)習(xí)操作系統(tǒng)的時(shí)候,我們應(yīng)該都學(xué)習(xí)過(guò)臨界區(qū)、互斥鎖這些概念,用于在并發(fā)環(huán)境下保證狀態(tài)的正確性。比如在秒殺時(shí),100 個(gè)用戶同時(shí)搶 10 個(gè)電腦,為了避免少賣或者超賣,就需要使用鎖來(lái)進(jìn)行并發(fā)控制。在 Go語(yǔ)言 里面互斥鎖是 sync.Mutex ,我們本篇文章就來(lái)學(xué)習(xí)下為什么要使用互斥鎖、如何使用互斥鎖,以及使用時(shí)的常見(jiàn)問(wèn)題。

為什么要使用互斥鎖

我們來(lái)看一個(gè)示例:我們起了 10000 個(gè)協(xié)程將變量 num 加1,因此肯定會(huì)存在并發(fā),如果我們不控制并發(fā),10000 個(gè)協(xié)程都執(zhí)行完后,該變量的值很大概率不等于 10000。

那么為什么會(huì)出現(xiàn)這個(gè)問(wèn)題呢,原因是 num++ 不是原子操作,它會(huì)先讀取變量 num 當(dāng)前值,然后對(duì)這個(gè)值 加1,再把結(jié)果保存到 num 中。例如 10 個(gè) goroutine 同時(shí)運(yùn)行到 num++ 這一行,可能同時(shí)讀取 num=1000,都加1后再保存, num=1001,這就與想要的結(jié)果不符。

package?main

import?(
?"fmt"
?"sync"
)

func?main()?{
?num?:=?0

?var?wg?sync.WaitGroup
?threadCount?:=?10000
?wg.Add(threadCount)
??
?for?i?:=?0;?i?<?threadCount;?i++?{
??go?func()?{
???defer?wg.Done()
???num++
??}()
?}
??
?wg.Wait()?//?等待?10000?個(gè)協(xié)程都執(zhí)行完
??fmt.Println(num)?//?9388(每次都可能不一樣)

}

我們?nèi)绻褂昧嘶コ怄i,可以保證每次進(jìn)入臨界區(qū)的只有一個(gè) goroutine,一個(gè) goroutine 執(zhí)行完后,另一個(gè) goroutine 才能進(jìn)入臨界區(qū)執(zhí)行,最終就實(shí)現(xiàn)了并發(fā)控制。

并發(fā)獲取鎖示意圖

package?main

import?(
?"fmt"
?"sync"
)

func?main()?{
?num?:=?0
?var?mutex?sync.Mutex??//?互斥鎖

?var?wg?sync.WaitGroup
?threadCount?:=?10000
?wg.Add(threadCount)
?for?i?:=?0;?i?<?threadCount;?i++?{
??go?func()?{
???defer?wg.Done()
???
???mutex.Lock()?//?加鎖
???num++?//?臨界區(qū)
???mutex.Unlock()?//?解鎖
???
??}()
?}

?wg.Wait()
?fmt.Println(num)?//?10000

}

如何使用互斥鎖

Mutex 保持 Go 一貫的簡(jiǎn)潔風(fēng)格,開(kāi)箱即用,聲明一個(gè)變量默認(rèn)是沒(méi)有加鎖的,加鎖使用 Lock() 方法,解鎖使用 Unlock() 方法。

使用方式一:直接聲明使用

這個(gè)在上例中已經(jīng)體現(xiàn)了,直接看上面的例子就好

使用方式二:封裝在其他結(jié)構(gòu)體中

我們可以將 Mutex 封裝在 struct 中,封裝成線程安全的函數(shù)供外部調(diào)用。比如我們封裝了一個(gè)線程安全的計(jì)數(shù)器,調(diào)用 Add() 就加一,調(diào)用Count() 返回計(jì)數(shù)器的值。

package?main

import?(
?"fmt"
?"sync"
)


type?Counter?struct?{
?num???int
?mutex?sync.Mutex
}

//?加一操作,涉及到臨界區(qū)?num,加鎖解鎖
func?(counter?*Counter)?Add()?{
?counter.mutex.Lock()
?defer?counter.mutex.Unlock()
?counter.num++
}

//?返回?cái)?shù)量,涉及到臨界區(qū)?num,加鎖解鎖
func?(counter?*Counter)?Count()?int?{
?counter.mutex.Lock()
?defer?counter.mutex.Unlock()
?return?counter.num
}

func?main()?{
?threadCount?:=?10000
??
?var?counter?Counter
?var?wg?sync.WaitGroup
?
?wg.Add(threadCount)
?for?i?:=?0;?i?<?threadCount;?i++?{
??go?func()?{
???defer?wg.Done()
???counter.Add()
??}()
?}

?wg.Wait()?//?等待所有?goroutine?都執(zhí)行完
?fmt.Println(counter.Count())?//?10000

}

在 Go 中,map 結(jié)構(gòu)是不支持并發(fā)的,如果并發(fā)讀寫就會(huì) panic

//?運(yùn)行會(huì) panic,提示 fatal error: concurrent map writes
func?main()?{
?m?:=?make(map[string]string)
?var?wait?sync.WaitGroup
?wait.Add(1000)

?for?i?:=?0;?i?<?1000;?i++?{
??item?:=?fmt.Sprintf("%d",?i)
??go?func()?{
???wait.Done()
???m[item]?=?item
??}()
?}
?wait.Wait()
}

基于 Mutex ,我們可以實(shí)現(xiàn)一個(gè)線程安全的 map

import?(
?"fmt"
?"sync"
)

type?ConcurrentMap?struct?{
?mutex?sync.Mutex
?items?map[string]interface{}
}

func?(c?*ConcurrentMap)?Add(key?string,?value?interface{})?{
?c.mutex.Lock()
?defer?c.mutex.Unlock()
?c.items[key]?=?value
}

func?(c?*ConcurrentMap)?Remove(key?string)?{
?c.mutex.Lock()
?defer?c.mutex.Unlock()
?delete(c.items,?key)
}
func?(c?*ConcurrentMap)?Get(key?string)?interface{}?{
?c.mutex.Lock()
?defer?c.mutex.Unlock()
?return?c.items[key]
}

func?NewConcurrentMap()?ConcurrentMap?{
?return?ConcurrentMap{
??items:?make(map[string]interface{}),
?}
}

func?main()?{
?m?:=?NewConcurrentMap()
?var?wait?sync.WaitGroup
?wait.Add(1000)

?for?i?:=?0;?i?<?1000;?i++?{
??item?:=?fmt.Sprintf("%d",?i)
??go?func()?{
???wait.Done()
???m.Add(item,?item)
??}()
?}
?wait.Wait()
?fmt.Println(m.Get("100"))?//?100
}

當(dāng)然,基于互斥鎖 Mutex 實(shí)現(xiàn)的線程安全 map 并不是性能最好的,基于讀寫鎖 sync.RWMutex 和 分片 可以實(shí)現(xiàn)性能更好的、線程安全的 map,開(kāi)發(fā)中比較常用的并發(fā)安全 map 是 orcaman / concurrent-map(https://github.com/orcaman/concurrent-map)。

互斥鎖的常見(jiàn)問(wèn)題

從上面可以看出,Mutex 的使用過(guò)程方法比較簡(jiǎn)單,但還是有幾點(diǎn)需要注意:

1.Mutex是可以在 goroutine A 中加鎖,在 goroutine B 中解鎖的,但是在實(shí)際使用中,盡量保證在同一個(gè) goroutine 中加解鎖。比如 goroutine A 申請(qǐng)到了鎖,在處理臨界區(qū)資源的時(shí)候,goroutine B 把鎖釋放了,但是 A 以為自己還持有鎖,會(huì)繼續(xù)處理臨界區(qū)資源,就可能會(huì)出現(xiàn)問(wèn)題。

2.Mutex的加鎖解鎖基本都是成對(duì)出現(xiàn),為了解決忘記解鎖,可以使用 defer 語(yǔ)句,在加鎖后直接 defer mutex.Unlock();但是如果處理完臨界區(qū)資源后還有很多耗時(shí)操作,為了盡早釋放鎖,不建議使用 defer,而是在處理完臨界區(qū)資源后就調(diào)用 mutex.Unlock() 盡早釋放鎖。

//?邏輯復(fù)雜,可能會(huì)忘記釋放鎖
func?main()?{
?var?mutex?sync.Mutex
?mutex.Lock()

?if?***?{
??if?***?{
???//?處理臨界區(qū)資源
???mutex.Unlock()
???return
??}
??//?處理臨界區(qū)資源
??mutex.Unlock()
??return
?}

?//?處理臨界區(qū)資源
?mutex.Unlock()
?return
}


//?避免邏輯復(fù)雜忘記釋放鎖,使用?defer語(yǔ)句,成對(duì)出現(xiàn)
func?main()?{
?var?mutex?sync.Mutex
?mutex.Lock()
?defer?mutex.Unlock()

?if?***?{
??if?***?{
???//?處理臨界區(qū)資源
???return
??}
??//?處理臨界區(qū)資源
??return
?}

?//?處理臨界區(qū)資源
?return
}

3.Mutex 不能復(fù)制使用

Mutex 是有狀態(tài)的,比如我們對(duì)一個(gè) Mutex 加鎖后,再進(jìn)行復(fù)制操作,會(huì)把當(dāng)前的加鎖狀態(tài)也給復(fù)制過(guò)去,基于加鎖的 Mutex 再加鎖肯定不會(huì)成功。進(jìn)行復(fù)制操作可能聽(tīng)起來(lái)是一個(gè)比較低級(jí)的錯(cuò)誤,但是無(wú)意間可能就會(huì)犯這種錯(cuò)誤。

package?main

import?(
?"fmt"
?"sync"
)

type?Counter?struct?{
?mutex?sync.Mutex
?num???int
}

func?SomeFunc(c?Counter)?{
?c.mutex.Lock()
?defer?c.mutex.Unlock()
?c.num--
}

func?main()?{
?var?counter?Counter
?counter.mutex.Lock()
?defer?counter.mutex.Unlock()

?counter.num++
?//?Go都是值傳遞,這里復(fù)制了?counter,此時(shí)?counter.mutex?是加鎖狀態(tài),在?SomeFunc?無(wú)法再次加鎖,就會(huì)一直等待
?SomeFunc(counter)

}

以上就是初識(shí)Golang Mutex互斥鎖的使用的詳細(xì)內(nèi)容,更多關(guān)于Golang Mutex互斥鎖的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 使用Go?http重試請(qǐng)求的示例

    使用Go?http重試請(qǐng)求的示例

    開(kāi)發(fā)中對(duì)于http請(qǐng)求是經(jīng)常遇到,一般可能網(wǎng)絡(luò)延遲或接口返回超時(shí),這篇文章主要介紹了使用Go?http重試請(qǐng)求的示例,需要的朋友可以參考下
    2022-08-08
  • Go語(yǔ)言:打造優(yōu)雅數(shù)據(jù)庫(kù)單元測(cè)試的實(shí)戰(zhàn)指南

    Go語(yǔ)言:打造優(yōu)雅數(shù)據(jù)庫(kù)單元測(cè)試的實(shí)戰(zhàn)指南

    Go語(yǔ)言數(shù)據(jù)庫(kù)單元測(cè)試入門:聚焦高效、可靠的數(shù)據(jù)庫(kù)代碼驗(yàn)證!想要確保您的Go應(yīng)用數(shù)據(jù)層堅(jiān)如磐石嗎?本指南將手把手教您如何利用Go進(jìn)行數(shù)據(jù)庫(kù)單元測(cè)試,輕松揪出隱藏的bug,打造無(wú)懈可擊的數(shù)據(jù)處理邏輯,一起來(lái)探索吧!
    2024-01-01
  • Go語(yǔ)言中切片(slice)和數(shù)組(array)的區(qū)別詳解

    Go語(yǔ)言中切片(slice)和數(shù)組(array)的區(qū)別詳解

    Go語(yǔ)言中切片(slice)和數(shù)組(array)是兩種不同的數(shù)據(jù)結(jié)構(gòu),它們?cè)谟梅ê托袨樯嫌幸恍┲匾獏^(qū)別,所以本文就通過(guò)一些代碼示例給大家詳細(xì)的介紹一下Go語(yǔ)言中切片(slice)和數(shù)組(array)的區(qū)別,需要的朋友可以參考下
    2023-09-09
  • Go?1.21.0?新增結(jié)構(gòu)化日志記錄標(biāo)準(zhǔn)庫(kù)log/slog使用詳解

    Go?1.21.0?新增結(jié)構(gòu)化日志記錄標(biāo)準(zhǔn)庫(kù)log/slog使用詳解

    這篇文章主要為大家介紹了Go?1.21.0?新增結(jié)構(gòu)化日志記錄標(biāo)準(zhǔn)庫(kù)log/slog使用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-11-11
  • golang內(nèi)置net/http包的使用詳解

    golang內(nèi)置net/http包的使用詳解

    在Go語(yǔ)言中,內(nèi)置的net/http包提供了一種出色的方式來(lái)處理HTTP請(qǐng)求和響應(yīng),不僅功能強(qiáng)大,而且易于使用,本文將帶大家深入了解Go語(yǔ)言內(nèi)置的net/http包,揭示其強(qiáng)大的功能和用法,希望對(duì)大家有所幫助
    2023-11-11
  • Gin框架中異步任務(wù)的實(shí)現(xiàn)

    Gin框架中異步任務(wù)的實(shí)現(xiàn)

    Gin框架中的異步任務(wù)處理是指在Web應(yīng)用中以非阻塞的方式處理耗時(shí)操作或后臺(tái)任務(wù),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2024-11-11
  • Golang中int,?int8,?int16,?int32,?int64和uint區(qū)別淺析

    Golang中int,?int8,?int16,?int32,?int64和uint區(qū)別淺析

    go語(yǔ)言中的int的大小是和操作系統(tǒng)位數(shù)相關(guān)的,如果是32位操作系統(tǒng),int類型的大小就是4字節(jié),如果是64位操作系統(tǒng),int類型的大小就是8個(gè)字節(jié),下面這篇文章主要給大家介紹了關(guān)于Golang中int,?int8,?int16,?int32,?int64和uint區(qū)別的相關(guān)資料,需要的朋友可以參考下
    2022-11-11
  • Golang關(guān)鍵字select的常用用法總結(jié)

    Golang關(guān)鍵字select的常用用法總結(jié)

    這篇文章主要為大家詳細(xì)介紹了golang中select關(guān)鍵字的常用用法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-10-10
  • Go中recover與panic區(qū)別詳解

    Go中recover與panic區(qū)別詳解

    這篇文章主要介紹了Go中recover與panic區(qū)別詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-11-11
  • go與go mod命令使用方式以及遇到的問(wèn)題

    go與go mod命令使用方式以及遇到的問(wèn)題

    這篇文章主要介紹了go與go mod命令使用方式以及遇到的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-09-09

最新評(píng)論

北流市| 格尔木市| 绩溪县| 丰城市| 商洛市| 饶河县| 桐城市| 赣州市| 柘荣县| 天祝| 页游| 耿马| 江孜县| 如东县| 山阳县| 德钦县| 江山市| 永昌县| 三明市| 轮台县| 平潭县| 凉山| 万宁市| 普洱| 奉化市| 安顺市| 内江市| 永清县| 台安县| 岱山县| 滁州市| 珠海市| 望谟县| 江油市| 洪雅县| 龙陵县| 宜君县| 新营市| 河津市| 五峰| 榆中县|