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

詳解Golang中的Mutex并發(fā)原語(yǔ)

 更新時(shí)間:2023年03月28日 08:21:45   作者:陳明勇  
Mutex?是?Go?語(yǔ)言中互斥鎖的實(shí)現(xiàn),它是一種同步機(jī)制,用于控制多個(gè)?goroutine?之間的并發(fā)訪問(wèn)。本文將著重介紹?Go?的?Mutex?并發(fā)原語(yǔ),希望對(duì)大家有所幫助

前言

Go 語(yǔ)言以 高并發(fā) 著稱(chēng),其并發(fā)操作是重要特性之一。雖然并發(fā)可以提高程序性能和效率,但同時(shí)也可能帶來(lái) 競(jìng)態(tài)條件死鎖 等問(wèn)題。為了避免這些問(wèn)題,Go 提供了許多 并發(fā)原語(yǔ),例如 MutexRWMutex、WaitGroup、Channel 等,用于實(shí)現(xiàn)同步、協(xié)調(diào)和通信等操作。

本文將著重介紹 GoMutex 并發(fā)原語(yǔ),它是一種鎖類(lèi)型,用于實(shí)現(xiàn)共享資源互斥訪問(wèn)。

說(shuō)明:本文使用的代碼基于的 Go 版本:1.20.1

Mutex

基本概念

MutexGo 語(yǔ)言中互斥鎖的實(shí)現(xiàn),它是一種同步機(jī)制,用于控制多個(gè) goroutine 之間的并發(fā)訪問(wèn)。當(dāng)多個(gè) goroutine 嘗試同時(shí)訪問(wèn)同一個(gè)共享資源時(shí),可能會(huì)導(dǎo)致數(shù)據(jù)競(jìng)爭(zhēng)和其他并發(fā)問(wèn)題,因此需要使用互斥鎖來(lái)協(xié)調(diào)它們之間的訪問(wèn)。

在上述圖片中,我們可以將綠色部分看作是臨界區(qū)。當(dāng) g1 協(xié)程通過(guò) mutex 對(duì)臨界區(qū)進(jìn)行加鎖后,臨界區(qū)將會(huì)被鎖定。此時(shí)如果 g2 想要訪問(wèn)臨界區(qū),就會(huì)失敗并進(jìn)入阻塞狀態(tài),直到鎖被釋放,g2 才能拿到臨界區(qū)的訪問(wèn)權(quán)。

結(jié)構(gòu)體介紹

type Mutex struct {
    state int32
    sema  uint32
}

字段:

state

state 是一個(gè) int32 類(lèi)型的變量,它存儲(chǔ)著 Mutex 的各種狀態(tài)信息(未加鎖、被加鎖、喚醒狀態(tài)、饑餓狀態(tài)),不同狀態(tài)通過(guò)位運(yùn)算進(jìn)行計(jì)算。

sema

sema 是一個(gè)信號(hào)量,用于實(shí)現(xiàn) Mutex 的等待和喚醒機(jī)制。

方法:

Lock()

Lock() 方法用于獲取 Mutex 的鎖,如果 Mutex 已經(jīng)被其他的 goroutine 鎖定,則 Lock() 方法會(huì)一直阻塞,直到該 goroutine 獲取到鎖為止。

UnLock()

Unlock() 方法用于釋放 Mutex 的鎖,將 Mutex 的狀態(tài)設(shè)置為未鎖定的狀態(tài)。

TryLock()

Go 1.18 版本以后,sync.Mutex 新增一個(gè) TryLock() 方法,該方法為非阻塞式的加鎖操作,如果加鎖成功,返回 true,否則返回 false。

雖然 TryLock() 的用法確實(shí)存在,但由于其使用場(chǎng)景相對(duì)較少,因此在使用時(shí)應(yīng)該格外謹(jǐn)慎。TryLock() 方法注釋如下所示:

// Note that while correct uses of TryLock do exist, they are rare,
// and use of TryLock is often a sign of a deeper problem
// in a particular use of mutexes.

代碼示例

我們先來(lái)看一個(gè)有并發(fā)安全問(wèn)題的例子

package main

import (
   "fmt"
   "sync"
)

var cnt int

func main() {
   var wg sync.WaitGroup
   for i := 0; i < 10; i++ {
      wg.Add(1)
      go func() {
         defer wg.Done()
         for j := 0; j < 10000; j++ {
            cnt++
         }
      }()
   }
   wg.Wait()
   fmt.Println(cnt)
}

在這個(gè)例子中,預(yù)期的 cnt 結(jié)果為 10 * 10000 = 100000。但是由于多個(gè) goroutine 并發(fā)訪問(wèn)了共享變量 cnt,并且沒(méi)有進(jìn)行任何同步操作,可能導(dǎo)致讀寫(xiě)沖突(race condition),從而影響 cnt 的值和輸出結(jié)果的正確性。這種情況下,不能確定最終輸出的 cnt 值是多少,每次執(zhí)行程序得到的結(jié)果可能不同。

在這種情況下,可以使用互斥鎖(sync.Mutex)來(lái)保護(hù)共享變量的訪問(wèn),保證只有一個(gè) goroutine 能夠同時(shí)訪問(wèn) cnt,從而避免競(jìng)態(tài)條件的問(wèn)題。修改后的代碼如下:

package main

import (
   "fmt"
   "sync"
)

var cnt int
var mu sync.Mutex

func main() {
   var wg sync.WaitGroup
   for i := 0; i < 10; i++ {
      wg.Add(1)
      go func() {
         defer wg.Done()
         for j := 0; j < 10000; j++ {
            mu.Lock()
            cnt++
            mu.Unlock()
         }
      }()
   }
   wg.Wait()
   fmt.Println(cnt)
}

在這個(gè)修改后的版本中,使用互斥鎖來(lái)保護(hù)共享變量 cnt 的訪問(wèn),可以避免出現(xiàn)競(jìng)態(tài)條件的問(wèn)題。具體而言,在 cnt++ 操作前,先執(zhí)行 Lock() 方法,以確保當(dāng)前 goroutine 獲取到了互斥鎖并且獨(dú)占了共享變量的訪問(wèn)權(quán)。在 cnt++ 操作完成后,再執(zhí)行 Unlock() 方法來(lái)釋放互斥鎖,從而允許其他 goroutine 獲取互斥鎖并訪問(wèn)共享變量。這樣,只有一個(gè) goroutine 能夠同時(shí)訪問(wèn) cnt,從而確保了最終輸出結(jié)果的正確性。

易錯(cuò)場(chǎng)景

忘記解鎖

如果使用 Lock() 方法之后,沒(méi)有調(diào)用 Unlock() 解鎖,會(huì)導(dǎo)致其他 goroutine 被永久阻塞。例如:

package main

import (
   "fmt"
   "sync"
   "time"
)

var mu sync.Mutex
var cnt int

func main() {
   go increase(1)
   go increase(2)

   time.Sleep(time.Second)
   fmt.Println(cnt)
}

func increase(delta int) {
   mu.Lock()
   cnt += delta
}

在上述代碼中,通常情況下,cnt 的結(jié)果應(yīng)該為 3。然而沒(méi)有解鎖操作,其中一個(gè) goroutine 被阻塞,導(dǎo)致沒(méi)有達(dá)到預(yù)期效果,最終輸出的 cnt 可能只能為 12

正確的做法是使用 defer 語(yǔ)句在函數(shù)返回前釋放鎖。

func increase(delta int) {
   mu.Lock()
   defer mu.Unlock() // 通過(guò) defer 語(yǔ)句在函數(shù)返回前釋放鎖
   cnt += delta
}

重復(fù)加鎖

重復(fù)加鎖操作被稱(chēng)為可重入操作。不同于其他一些編程語(yǔ)言的鎖實(shí)現(xiàn)(例如 JavaReentrantLock),Gomutex 并不支持可重入操作,如果發(fā)生了重復(fù)加鎖操作,就會(huì)導(dǎo)致死鎖。例如:

package main

import (
   "fmt"
   "sync"
   "time"
)

var mu sync.Mutex
var cnt int

func main() {
   go increase(1)
   go increase(2)

   time.Sleep(time.Second)
   fmt.Println(cnt)
}

func increase(delta int) {
   mu.Lock()
   mu.Lock()
   cnt += delta
   mu.Unlock()
}

在這個(gè)例子中,如果在 increase 函數(shù)中重復(fù)加鎖,將會(huì)導(dǎo)致 mu 鎖被第二次鎖住,而其他 goroutine 將被永久阻塞,從而導(dǎo)致程序死鎖。正確的做法是只對(duì)需要加鎖的代碼段進(jìn)行加鎖,避免重復(fù)加鎖。

基于 Mutex 實(shí)現(xiàn)一個(gè)簡(jiǎn)單的線程安全的緩存

import "sync"

type Cache struct {
   data map[string]any
   mu   sync.Mutex
}

func (c *Cache) Get(key string) (any, bool) {
   c.mu.Lock()
   defer c.mu.Unlock()
   value, ok := c.data[key]
   return value, ok
}

func (c *Cache) Set(key string, value any) {
   c.mu.Lock()
   defer c.mu.Unlock()
   c.data[key] = value
}

上述代碼實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的線程安全的緩存。使用 Mutex 可以保證同一時(shí)刻只有一個(gè) goroutine 進(jìn)行讀寫(xiě)操作,避免多個(gè) goroutine 并發(fā)讀寫(xiě)同一數(shù)據(jù)時(shí)產(chǎn)生數(shù)據(jù)不一致性的問(wèn)題。

對(duì)于緩存場(chǎng)景,讀操作比寫(xiě)操作更頻繁,因此使用 RWMutex 代替 Mutex 會(huì)更好,因?yàn)?RWMutex 允許多個(gè) goroutine 同時(shí)進(jìn)行讀操作,只有在寫(xiě)操作時(shí)才會(huì)進(jìn)行互斥鎖定,從而減少了鎖的競(jìng)爭(zhēng),提高了程序的并發(fā)性能。后續(xù)文章會(huì)對(duì) RWMutex 進(jìn)行介紹。

小結(jié)

本文主要介紹了 Go 語(yǔ)言中互斥鎖 Mutex 的概念、對(duì)應(yīng)的字段和方法、基本使用和易錯(cuò)場(chǎng)景,最后基于 Mutex 實(shí)現(xiàn)一個(gè)簡(jiǎn)單的線程安全的緩存。

Mutex 是保證共享資源數(shù)據(jù)一致性的重要手段,但使用不當(dāng)會(huì)導(dǎo)致性能下降或死鎖等問(wèn)題。因此,在使用 Mutex 時(shí)需要仔細(xì)考慮代碼的設(shè)計(jì)和并發(fā)場(chǎng)景,發(fā)揮 Mutex 的最大作用。

到此這篇關(guān)于詳解Golang中的Mutex并發(fā)原語(yǔ)的文章就介紹到這了,更多相關(guān)Golang Mutex并發(fā)原語(yǔ)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 向Rust學(xué)習(xí)Go考慮簡(jiǎn)單字符串插值特性示例解析

    向Rust學(xué)習(xí)Go考慮簡(jiǎn)單字符串插值特性示例解析

    這篇文章主要為大家介紹了向Rust學(xué)習(xí)Go考慮簡(jiǎn)單字符串插值特性示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-02-02
  • golang的強(qiáng)制類(lèi)型轉(zhuǎn)換實(shí)現(xiàn)

    golang的強(qiáng)制類(lèi)型轉(zhuǎn)換實(shí)現(xiàn)

    這篇文章主要介紹了golang的強(qiáng)制類(lèi)型轉(zhuǎn)換實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-02-02
  • 詳解MongoDB?Go?Driver如何記錄日志

    詳解MongoDB?Go?Driver如何記錄日志

    這篇文章主要為大家介紹了MongoDB?Go?Driver如何記錄日志詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-08-08
  • 使用go進(jìn)行云存儲(chǔ)上傳實(shí)現(xiàn)實(shí)例

    使用go進(jìn)行云存儲(chǔ)上傳實(shí)現(xiàn)實(shí)例

    這篇文章主要為大家介紹了使用go進(jìn)行云存儲(chǔ)上傳實(shí)例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪<BR>
    2024-01-01
  • Golang map實(shí)現(xiàn)原理深入分析

    Golang map實(shí)現(xiàn)原理深入分析

    map是一種無(wú)序的基于key-value的數(shù)據(jù)結(jié)構(gòu),Go語(yǔ)言中的map是引用類(lèi)型,必須初始化才能使用,下面這篇文章主要給大家介紹了關(guān)于golang中map使用的幾點(diǎn)注意事項(xiàng),需要的朋友可以參考下
    2023-01-01
  • Go語(yǔ)言學(xué)習(xí)網(wǎng)絡(luò)編程與Http教程示例

    Go語(yǔ)言學(xué)習(xí)網(wǎng)絡(luò)編程與Http教程示例

    這篇文章主要為大家介紹了Go語(yǔ)言學(xué)習(xí)網(wǎng)絡(luò)編程與Http教程示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-03-03
  • 通過(guò)Go channel批量讀取數(shù)據(jù)的示例詳解

    通過(guò)Go channel批量讀取數(shù)據(jù)的示例詳解

    批量處理的主要邏輯是:從 channel 中接收數(shù)據(jù),積累到一定數(shù)量或者達(dá)到時(shí)間限制后,將數(shù)據(jù)批量處理(例如發(fā)送到 Kafka 或者寫(xiě)入網(wǎng)絡(luò)),下面我將展示一個(gè)從 Go channel 中批量讀取數(shù)據(jù),并批量發(fā)送到 Kafka 和批量寫(xiě)入網(wǎng)絡(luò)數(shù)據(jù)的示例,需要的朋友可以參考下
    2024-10-10
  • 詳解如何修改Go結(jié)構(gòu)體的私有字段

    詳解如何修改Go結(jié)構(gòu)體的私有字段

    在 Go 語(yǔ)言中,結(jié)構(gòu)體字段的訪問(wèn)權(quán)限是由字段名的首字母決定的:首字母大寫(xiě)表示公共字段(public),首字母小寫(xiě)表示私有字段(private),本文給大家介紹了如何修改Go結(jié)構(gòu)體的私有字段,需要的朋友可以參考下
    2025-01-01
  • GO實(shí)現(xiàn)跳躍表的示例詳解

    GO實(shí)現(xiàn)跳躍表的示例詳解

    跳表全稱(chēng)叫做跳躍表,簡(jiǎn)稱(chēng)跳表,是一個(gè)隨機(jī)化的數(shù)據(jù)結(jié)構(gòu),實(shí)質(zhì)就是一種可以進(jìn)行二分查找的有序鏈表。本文將利用GO語(yǔ)言編寫(xiě)一個(gè)跳表,需要的可以參考一下
    2022-12-12
  • 簡(jiǎn)單聊聊Golang中defer預(yù)計(jì)算參數(shù)

    簡(jiǎn)單聊聊Golang中defer預(yù)計(jì)算參數(shù)

    在golang當(dāng)中defer代碼塊會(huì)在函數(shù)調(diào)用鏈表中增加一個(gè)函數(shù)調(diào)用,下面這篇文章主要給大家介紹了關(guān)于Golang中defer預(yù)計(jì)算參數(shù)的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-03-03

最新評(píng)論

嵊州市| 隆德县| 金坛市| 封开县| 织金县| 资源县| 昭平县| 龙门县| 同江市| 长兴县| 鸡东县| 民和| 孟连| 延川县| 镇平县| 丹阳市| 辽中县| 新巴尔虎左旗| 晋江市| 温州市| 交城县| 凤庆县| 天长市| 司法| 调兵山市| 扎鲁特旗| 特克斯县| 彝良县| 安徽省| 镇坪县| 广饶县| 娄烦县| 龙里县| 乌审旗| 习水县| 泌阳县| 泸水县| 伊通| 罗山县| 察雅县| 固安县|