91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Golang中的Mutex怎么使用

發布時間:2023-05-10 17:25:08 來源:億速云 閱讀:123 作者:iii 欄目:開發技術

本篇內容介紹了“Golang中的Mutex怎么使用”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!

    Mutex

    基本概念

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

    Golang中的Mutex怎么使用

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

    結構體介紹

    type Mutex struct {
        state int32
        sema  uint32
    }

    字段:

    state

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

    sema

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

    方法:

    Lock()

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

    UnLock()

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

    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.

    代碼示例

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

    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 結果為 10 * 10000 = 100000。但是由于多個 goroutine 并發訪問了共享變量 cnt,并且沒有進行任何同步操作,可能導致讀寫沖突(race condition),從而影響 cnt 的值和輸出結果的正確性。這種情況下,不能確定最終輸出的 cnt 值是多少,每次執行程序得到的結果可能不同。

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

    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 的訪問,可以避免出現競態條件的問題。具體而言,在 cnt++ 操作前,先執行 Lock() 方法,以確保當前 goroutine 獲取到了互斥鎖并且獨占了共享變量的訪問權。在 cnt++ 操作完成后,再執行 Unlock() 方法來釋放互斥鎖,從而允許其他 goroutine 獲取互斥鎖并訪問共享變量。這樣,只有一個 goroutine 能夠同時訪問 cnt,從而確保了最終輸出結果的正確性。

    易錯場景

    忘記解鎖

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

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

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

    重復加鎖

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

    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 函數中重復加鎖,將會導致 mu 鎖被第二次鎖住,而其他 goroutine 將被永久阻塞,從而導致程序死鎖。正確的做法是只對需要加鎖的代碼段進行加鎖,避免重復加鎖。

    基于 Mutex 實現一個簡單的線程安全的緩存

    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
    }

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

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

    “Golang中的Mutex怎么使用”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!

    向AI問一下細節

    免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

    AI

    柳林县| 宝丰县| 平定县| 宣化县| 盱眙县| 崇仁县| 香河县| 泾阳县| 郑州市| 前郭尔| 漯河市| 岚皋县| 睢宁县| 广东省| 黄陵县| 林西县| 寿光市| 玉环县| 栖霞市| 德保县| 延川县| 独山县| 仁寿县| 左贡县| 田东县| 沐川县| 枝江市| 临沂市| 漳平市| 鄂托克前旗| 治多县| 墨玉县| 郴州市| 明水县| 海伦市| 弥渡县| 文登市| 黄梅县| 新泰市| 乌兰察布市| 太湖县|