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

Go語言如何利用Mutex保障數(shù)據(jù)讀寫正確

 更新時(shí)間:2023年05月11日 15:46:03   作者:程序員祝融  
這篇文章主要介紹了互斥鎖的實(shí)現(xiàn)機(jī)制,以及?Go?標(biāo)準(zhǔn)庫的互斥鎖?Mutex?的基本使用方法,文中的示例代碼講解詳細(xì),需要的小伙伴可以參考一下

Go 并發(fā)場景下如何保障數(shù)據(jù)讀寫正確?本文聊聊 Mutex 的用法。

Go 語言作為一個原生支持用戶態(tài)進(jìn)程(Goroutine)的語言,當(dāng)提到并發(fā)編程、多線程編程時(shí),往往都離不開鎖這一概念。鎖是一種并發(fā)編程中的同步原語(Synchronization Primitives),它能保證多個 Goroutine 在訪問同一片內(nèi)存時(shí)不會出現(xiàn)競爭條件(Race condition)等問題。

本文,我會帶你詳細(xì)了解互斥鎖的實(shí)現(xiàn)機(jī)制,以及 Go 標(biāo)準(zhǔn)庫的互斥鎖 Mutex 的基本使用方法。后面會講解 Mutex 的具體實(shí)現(xiàn)原理、易錯場景和一些拓展用法。 歡迎關(guān)注一下不迷路。

好了,我們先來看看互斥鎖的實(shí)現(xiàn)機(jī)制。

1、實(shí)現(xiàn)機(jī)制

互斥鎖 Mutex 是并發(fā)控制的一個基本手段,是為了避免并發(fā)競爭建立的并發(fā)控制機(jī)制,其中有個“臨界區(qū)”的概念。

在并發(fā)編程過程中,如果程序中一部分資源或者變量會被并發(fā)訪問或者修改,為了避免并發(fā)訪問導(dǎo)致數(shù)據(jù)的不準(zhǔn)確,這部分程序需要率先被保護(hù)起來,之后操作,操作結(jié)束后去除保護(hù),這部分被保護(hù)的程序就叫做 臨界區(qū)。

限定臨界區(qū)只能同時(shí)由一個線程持有。 當(dāng)臨界區(qū)由一個線程持有的時(shí)候,其它線程如果想進(jìn)入這個臨界區(qū),就會返回失敗,或者是等待。直到持有的線程退出臨界區(qū),其他線程才有機(jī)會獲得這個臨界區(qū)。如下圖:

Go mutex 臨界區(qū)示意圖

上圖互斥鎖就很好地解決了資源競爭問題,有人也把互斥鎖叫做排它鎖。那在 Go 標(biāo)準(zhǔn)庫中,它提供了 Mutex 來實(shí)現(xiàn)互斥鎖這個功能。

Go 語言在 sync 包中提供了用于同步的一些基本原語,包括常見的 sync.Mutex、sync.RWMutex、sync.WaitGroup、sync.Once 和 sync.Cond。這次主要講 Mutex。

接下來我們看看到底可以怎么使用 Mutex。

2、基本用法

在 Go 的標(biāo)準(zhǔn)庫中,package sync 提供了鎖相關(guān)的一系列同步原語,這個 package 還定義了一個 Locker 的接口,Mutex 就實(shí)現(xiàn)了這個接口。

互斥鎖 Mutex 提供了兩個方法 Lock 和 Unlock:進(jìn)入到臨界區(qū)使用 Lock 方法加鎖,退出臨界區(qū)使用 Unlock 方法釋放鎖。

type Locker interface {
    Lock()
    Unlock()
}

上面可以看出,Go 定義的鎖接口的方法集很簡單,就是請求鎖(Lock)和釋放鎖(Unlock)這兩個方法,繼承了 Go 語言一貫的簡潔風(fēng)格。

我們本文會介紹的 Mutex 以及后面會介紹的讀寫鎖 RWMutex 都實(shí)現(xiàn)了 Locker 接口,所以首先我把這個接口介紹了,提前了解一下。

func(m *Mutex)Lock()
func(m *Mutex)Unlock()

并發(fā)場景下,一個 goroutine 調(diào)用 Lock 方法拿到鎖后,此時(shí)其他的 goroutine 會阻塞在 Lock 的調(diào)用上,一直等到當(dāng)前獲取到鎖的 goroutine 釋放鎖。

看到這兒,你可能會問,為啥一定要加鎖呢?那我們就說一下在并發(fā)場景下不使用鎖的例子,看下會出現(xiàn)什么問題。

舉一個計(jì)數(shù)器的例子,是由 10 個 goroutine 對計(jì)數(shù)器進(jìn)行累加操作,每個 goroutine 負(fù)責(zé)執(zhí)行 10 萬次的加 1 操作,期望的結(jié)果是 1000000 (10 * 100000)。

package main
import (
    "fmt"
    "sync"
)
    
func main() {
    var count = 0
    // 使用 WaitGroup 等待,創(chuàng)建 10 個goroutine
    var wg sync.WaitGroup
    wg.Add(10)
    for i := 0; i< 10;i++ {
        go func() {
            defer wg.Done()
            // 對變量count執(zhí)行10次加1
            for j := 0; j< 100000; j++ {
                count++
            }
        }()
    }
    // 等待 10個 goroutine完成
    wg.Wait()
    fmt.Printin("count:", count)
}

每次運(yùn)行,都得到了不同的結(jié)果,所以是不會得到期望的 1000000。

那么這是為什么?

其實(shí),因?yàn)?count++ 不是一個原子操作,就可能有并發(fā)的問題。

上述是并發(fā)訪問共享數(shù)據(jù)的常見錯誤,10 個 goroutine 同時(shí)讀取到 count 的值為 9867,對值加 1,值變成 啦9868,然后把這個值覆蓋到 count,但是實(shí)際上此時(shí)我們增加的總數(shù)應(yīng)該是 10 才對,這里卻只增加了 1,好多計(jì)數(shù)都被“吞”掉了。

3、race detector

很多時(shí)候,并發(fā)問題隱藏得非常深,即使是有經(jīng)驗(yàn)的人,也不太容易發(fā)現(xiàn)或者 Debug 出來。

Go race detector , 一個檢測并發(fā)訪問共享資源是否有問題的工具,它可以幫助我們自動發(fā)現(xiàn)程序有沒有 data race 的問題。是基于 Google 的 C/C++ sanitizers 技術(shù)實(shí)現(xiàn)的,能夠監(jiān)測出內(nèi)存地址的訪問,當(dāng)代碼運(yùn)行時(shí),race detector 可以很好的監(jiān)控到共享變量的非同步訪問,出現(xiàn) race 的時(shí)候,能夠輸出警告的信息。

怎么用的呢?

在編譯、測試、運(yùn)行 Go 代碼的時(shí)候,加上 race 參數(shù),就有可能發(fā)現(xiàn)并發(fā)問題。比如在上面的例子中,我們可以加上 race 參數(shù)運(yùn)行,檢測一下是不是有并發(fā)問題。

go run -race main.go 就會輸出警告信息。

圖中會提示有并發(fā)問題,會提示哪一個 goroutine 在某一行對變量有寫操作,同時(shí)也會提示哪個 goroutine 在某一行對變量有讀操作,這就是并發(fā)操作時(shí)引起了 data race。

既然存在 data race 問題,我們怎么去解決呢?接下來就講下 Mutex,它可以輕松地消除掉 data race。

package main
import (
    "fmt"
    "sync"
)
    
func main() {
    var count = 0
    
    // 互斥鎖保護(hù)計(jì)數(shù)器
    var mu sync.Mutex
    // 輔助變量,用來確認(rèn)所有的goroutine都完成
    var wg sync.WaitGroup
    wg.Add(10)
    // 啟動10個gourontine
    for i := 0;i< 10;i+++ {
        go func() {
            defer wg.Done()
            for j := 0; j< 100000; j++ {
                mu.Lock()
                count++
                mu.Unlock()
            }
        }()
    }
    // 等待 10個 goroutine完成
    wg.Wait()
    fmt.Printin("count:", count)
}

運(yùn)行一下 go run -race main.go

你會發(fā)現(xiàn)輸出了期望值 1000000,data race 告警也沒有啦。

怎么樣,是不是很驚喜,使用 Mutex 是不是非常高效?

我們在日常使用中,Mutex 會嵌入到其它 struct 中使用。

type Counter struct{
    sync.Mutex
    Count uint64
}


func main() {
    var counter Counter
    var wg sync.WaitGroup
    wg.Add(10)
    for i := 0;i< 10;i++ {
        go func() {
            defer wg.Done()
            for j := 0; j < 100000; j++ {
                counter.Lock()
                counter.Count++
                counter.Unlock()
            }
        }()
    }
    wg.Wait()
    fmt.Println("count:", counter.Count)
}

當(dāng)嵌入的 struct 有多個字段,我們會把 Mutex 放在要控制的字段上面,然后使用空格把字段分隔開來。這樣寫的話,邏輯會更清晰,也更易于維護(hù)。

有時(shí)候,你還可以把獲取鎖、釋放鎖、計(jì)數(shù)加一的邏輯封裝成一個方法,對外不需要暴露鎖等邏輯。

//線程安全的計(jì)數(shù)器類型
type Counter struct{
    CounterType int
    Name        string
    mu    sync.Mutex
    count uint64
}

func main() {
    // 封裝一個計(jì)數(shù)器
    var counter Counter
    var wg sync.WaitGroup
    wg.Add(10)
    // 啟動 10 個 goroutine
    for i := 0;i< 10;i++ {
        go func() {
            defer wg.Done( )
            // 執(zhí)行 10 萬次累加
            for j := 0; j< 100000; j++ {
                // 受到鎖保護(hù)的方法
                counter.Incr()
            }
        }()
    }
    wg.Wait()
    fmt.PrintIn(counter.Count())
}

// 加1的方法,內(nèi)部使用互斥鎖保護(hù)
func (c *Counter) Incr() {
    c.mu.Lock()
    c.count++
    c.mu.Unlock()
}
// 得到計(jì)數(shù)器的值,也需要鎖保護(hù)
func (c *Counter) Count() uint64 {
    c.mu.Lock()
    defer c.mu.Unlock()
    return c.count
}

4、總結(jié)

本文介紹了并發(fā)問題的背景知識、標(biāo)準(zhǔn)庫中 Mutex 的使用,通過 Go race detector 工具發(fā)下并發(fā)場景下的問題及解決方法。你肯定已經(jīng)了解了 Mutex 這個同步原語。

日常開發(fā)中,在設(shè)計(jì)階段,我們就應(yīng)該需要考慮共享資源的并發(fā)問題,當(dāng)然在初始階段有時(shí)候并不是很確定某個資源時(shí)否會唄共享,會隨著后續(xù)的迭代會顯現(xiàn)。雖遲但會到。當(dāng)你意識到這個問題時(shí),就需要通過互斥鎖來解決啦。

其實(shí) Docker issue 37583、35517、32826、30696等、kubernetes issue 72361、71617等,都是后來發(fā)現(xiàn)的 data race 而采用互斥鎖 Mutex 進(jìn)行修復(fù)的。

5、思考問題

Q: 當(dāng) Mutex 已經(jīng)被一個 goroutine 獲取了鎖,其它的 goroutine 們只能一直等待。當(dāng)這個鎖釋放后,等待中的 goroutine 中哪一個會優(yōu)先獲取 Mutex 呢?

到此這篇關(guān)于Go語言如何利用Mutex保障數(shù)據(jù)讀寫正確的文章就介紹到這了,更多相關(guān)Go語言Mutex保障數(shù)據(jù)讀寫正確內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Golang利用casbin實(shí)現(xiàn)權(quán)限驗(yàn)證詳解

    Golang利用casbin實(shí)現(xiàn)權(quán)限驗(yàn)證詳解

    Casbin是一個強(qiáng)大的、高效的開源訪問控制框架,其權(quán)限管理機(jī)制支持多種訪問控制模型,Casbin只負(fù)責(zé)訪問控制。本文將利用casbin實(shí)現(xiàn)權(quán)限驗(yàn)證功能,需要的可以參考一下
    2023-02-02
  • 一文吃透Go的內(nèi)置RPC原理

    一文吃透Go的內(nèi)置RPC原理

    這篇文章主要為大家詳細(xì)介紹了Go語言中內(nèi)置RPC的原理。說起?RPC?大家想到的一般是框架,Go?作為編程語言竟然還內(nèi)置了?RPC,著實(shí)讓我有些吃鯨,本文就來一起聊聊吧
    2023-03-03
  • 使用Golang的singleflight防止緩存擊穿的方法

    使用Golang的singleflight防止緩存擊穿的方法

    這篇文章主要介紹了使用Golang的singleflight防止緩存擊穿的方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-04-04
  • Go語言學(xué)習(xí)otns示例分析

    Go語言學(xué)習(xí)otns示例分析

    這篇文章主要為大家介紹了Go語言學(xué)習(xí)otns示例分析詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-04-04
  • Golang定時(shí)器的2種實(shí)現(xiàn)方法與區(qū)別

    Golang定時(shí)器的2種實(shí)現(xiàn)方法與區(qū)別

    這篇文章主要給大家介紹了關(guān)于Golang定時(shí)器的2種實(shí)現(xiàn)方法與區(qū)別的相關(guān)資料,文中通過圖文介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-02-02
  • Go語言報(bào)錯:'godoc'?不是內(nèi)部或外部命令,也不是可運(yùn)行的程序(godoc無法使用處理)解決方法

    Go語言報(bào)錯:'godoc'?不是內(nèi)部或外部命令,也不是可運(yùn)行的程序(godoc無法使用處理)解決

    這篇文章主要介紹了Go語言報(bào)錯:'godoc'?不是內(nèi)部或外部命令,也不是可運(yùn)行的程序(godoc無法使用處理)解決方法,詳細(xì)描述了Go語言godoc命令無法使用的原因、解決方法與相關(guān)注意事項(xiàng),需要的朋友可以參考下
    2024-01-01
  • Golang中使用JSON的一些小技巧分享

    Golang中使用JSON的一些小技巧分享

    這篇文章主要分享了Golang中使用JSON的一些小技巧,文中通過示例代碼介紹的非常詳細(xì),對大家具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起看看吧。
    2017-06-06
  • Go REFLECT Library反射類型詳解

    Go REFLECT Library反射類型詳解

    這篇文章主要為大家介紹了Go REFLECT Library反射類型詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-08-08
  • 一起聊聊Go語言中的語法糖的使用

    一起聊聊Go語言中的語法糖的使用

    語法糖通常是用來簡化代碼編寫的,特性就是使用語法糖前后編譯的結(jié)果是相同的。這篇文章主要就來和大家一起聊聊Go語言中的語法糖的實(shí)現(xiàn)
    2022-07-07
  • 深入理解Go語言實(shí)現(xiàn)多態(tài)?

    深入理解Go語言實(shí)現(xiàn)多態(tài)?

    本文主要介紹了Go語言實(shí)現(xiàn)多態(tài),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-05-05

最新評論