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

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

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

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

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

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

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

1、實現(xiàn)機制

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

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

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

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

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

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

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

2、基本用法

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

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

type Locker interface {
    Lock()
    Unlock()
}

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

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

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

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

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

舉一個計數(shù)器的例子,是由 10 個 goroutine 對計數(shù)器進行累加操作,每個 goroutine 負責(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)
}

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

那么這是為什么?

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

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

3、race detector

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

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

怎么用的呢?

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

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

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

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

package main
import (
    "fmt"
    "sync"
)
    
func main() {
    var count = 0
    
    // 互斥鎖保護計數(shù)器
    var mu sync.Mutex
    // 輔助變量,用來確認所有的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)
}

運行一下 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)
}

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

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

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

func main() {
    // 封裝一個計數(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++ {
                // 受到鎖保護的方法
                counter.Incr()
            }
        }()
    }
    wg.Wait()
    fmt.PrintIn(counter.Count())
}

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

4、總結(jié)

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

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

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

5、思考問題

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

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

相關(guān)文章

  • Go語言并發(fā)處理效率響應(yīng)能力及在現(xiàn)代軟件開發(fā)中的重要性

    Go語言并發(fā)處理效率響應(yīng)能力及在現(xiàn)代軟件開發(fā)中的重要性

    這篇文章主要為大家介紹了Go語言并發(fā)處理的效率及響應(yīng)能力以及在現(xiàn)代軟件開發(fā)中的重要性實例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-12-12
  • GoLang內(nèi)存模型詳細講解

    GoLang內(nèi)存模型詳細講解

    go官方介紹go內(nèi)存模型的時候說:探究在什么條件下,goroutine 在讀取一個變量的值的時,能夠看到其它 goroutine 對這個變量進行的寫的結(jié)果,Go內(nèi)存模型規(guī)定了一些條件,在這些條件下,在一個goroutine中讀取變量返回的值能夠確保是另一個goroutine中對該變量寫入的值
    2022-12-12
  • Golang中Gin數(shù)據(jù)庫表名前綴的三種方法

    Golang中Gin數(shù)據(jù)庫表名前綴的三種方法

    本文主要介紹了Golang中Gin數(shù)據(jù)庫表名前綴的三種方法,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2025-02-02
  • 一文詳解Go語言io.LimitedReader類型

    一文詳解Go語言io.LimitedReader類型

    這篇文章主要為大家介紹了Go語言io.LimitedReader類型示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-07-07
  • 一文帶你深入理解Golang中的泛型

    一文帶你深入理解Golang中的泛型

    Go?在泛型方面一直被詬病,因為它在這方面相對比較落后。但是,在?Go?1.18?版本中,泛型已經(jīng)被正式引入,成為了?Go?語言中一個重要的特性。本文將會詳細介紹?Go?泛型的相關(guān)概念,語法和用法,希望能夠幫助大家更好地理解和應(yīng)用這一特性
    2023-05-05
  • Golang利用Template模板動態(tài)生成文本

    Golang利用Template模板動態(tài)生成文本

    Go語言中的Go?Template是一種用于生成文本輸出的簡單而強大的模板引擎,它提供了一種靈活的方式來生成各種格式的文本,下面我們就來看看具體如何使用Template實現(xiàn)動態(tài)文本生成吧
    2023-09-09
  • Go語言基礎(chǔ)go install命令使用示例詳解

    Go語言基礎(chǔ)go install命令使用示例詳解

    這篇文章主要為大家介紹了Go語言基礎(chǔ)go install命令的使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2021-11-11
  • 利用Go語言快速實現(xiàn)一個極簡任務(wù)調(diào)度系統(tǒng)

    利用Go語言快速實現(xiàn)一個極簡任務(wù)調(diào)度系統(tǒng)

    任務(wù)調(diào)度(Task Scheduling)是很多軟件系統(tǒng)中的重要組成部分,字面上的意思是按照一定要求分配運行一些通常時間較長的腳本或程序。本文將利用Go語言快速實現(xiàn)一個極簡任務(wù)調(diào)度系統(tǒng),感興趣的可以了解一下
    2022-10-10
  • go學(xué)習(xí)筆記讀取consul配置文件詳解

    go學(xué)習(xí)筆記讀取consul配置文件詳解

    這篇文章主要為大家介紹了go學(xué)習(xí)筆記讀取consul配置文件詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-05-05
  • 一文教你如何封裝安全的go

    一文教你如何封裝安全的go

    這篇文章主要給大家介紹了關(guān)于如何封裝安全go的相關(guān)資料,文中通過實例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2022-02-02

最新評論

区。| 邓州市| 渑池县| 贺兰县| 洛浦县| 越西县| 卫辉市| 北辰区| 桑植县| 应用必备| 儋州市| 平遥县| 兴业县| 巨鹿县| 天津市| 黄山市| 岗巴县| 巢湖市| 陵水| 鄂伦春自治旗| 瑞金市| 寿宁县| 明水县| 涟源市| 河北区| 和静县| 塔城市| 商丘市| 青铜峡市| 西和县| 五峰| 广西| 神木县| 天等县| 彰武县| 南昌县| 望江县| 琼中| 中方县| 绥滨县| 津南区|