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

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

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

前言

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

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

說明:本文使用的代碼基于的 Go 版本:1.20.1

Mutex

基本概念

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

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

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

type Mutex struct {
    state int32
    sema  uint32
}

字段:

state

state 是一個 int32 類型的變量,它存儲著 Mutex 的各種狀態(tài)信息(未加鎖、被加鎖、喚醒狀態(tài)、饑餓狀態(tài)),不同狀態(tài)通過位運算進行計算。

sema

sema 是一個信號量,用于實現(xiàn) Mutex 的等待和喚醒機制。

方法:

Lock()

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

UnLock()

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

TryLock()

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

雖然 TryLock() 的用法確實存在,但由于其使用場景相對較少,因此在使用時應該格外謹慎。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.

代碼示例

我們先來看一個有并發(fā)安全問題的例子

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)
}

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

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

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)
}

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

易錯場景

忘記解鎖

如果使用 Lock() 方法之后,沒有調(diào)用 Unlock() 解鎖,會導致其他 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é)果應該為 3。然而沒有解鎖操作,其中一個 goroutine 被阻塞,導致沒有達到預期效果,最終輸出的 cnt 可能只能為 12。

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

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

重復加鎖

重復加鎖操作被稱為可重入操作。不同于其他一些編程語言的鎖實現(xiàn)(例如 JavaReentrantLock),Gomutex 并不支持可重入操作,如果發(fā)生了重復加鎖操作,就會導致死鎖。例如:

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()
}

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

基于 Mutex 實現(xià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
}

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

對于緩存場景,讀操作比寫操作更頻繁,因此使用 RWMutex 代替 Mutex 會更好,因為 RWMutex 允許多個 goroutine 同時進行讀操作,只有在寫操作時才會進行互斥鎖定,從而減少了鎖的競爭,提高了程序的并發(fā)性能。后續(xù)文章會對 RWMutex 進行介紹。

小結(jié)

本文主要介紹了 Go 語言中互斥鎖 Mutex 的概念、對應的字段和方法、基本使用和易錯場景,最后基于 Mutex 實現(xiàn)一個簡單的線程安全的緩存。

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

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

相關(guān)文章

  • 向Rust學習Go考慮簡單字符串插值特性示例解析

    向Rust學習Go考慮簡單字符串插值特性示例解析

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

    golang的強制類型轉(zhuǎn)換實現(xiàn)

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

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

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

    使用go進行云存儲上傳實現(xiàn)實例

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

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

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

    Go語言學習網(wǎng)絡編程與Http教程示例

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

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

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

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

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

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

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

    簡單聊聊Golang中defer預計算參數(shù)

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

最新評論

恩平市| 岳池县| 彭水| 喀什市| 定日县| 永善县| 广平县| 泰兴市| 类乌齐县| 绥德县| 长沙市| 静宁县| 阿拉善盟| 饶平县| 泾川县| 曲阳县| 公安县| 台山市| 曲阜市| 永平县| 蒲城县| 通渭县| 桂阳县| 西丰县| 密云县| 康马县| 遂昌县| 穆棱市| 苗栗县| 西城区| 项城市| 垦利县| 台南市| 西充县| 同德县| 汶川县| 新乐市| 永福县| 左贡县| 达州市| 尼木县|