首頁  >  文章  >  後端開發  >  關於 Go 並發程式設計 Mutex

關於 Go 並發程式設計 Mutex

藏色散人
藏色散人轉載
2020-11-16 13:53:522537瀏覽

友情提示:這篇文章大約需要閱讀 5分鐘45秒,不足之處請多指教,感謝你的閱讀。 我們比較常見的大型專案的設計中都會出現並發存取問題,並發就是為了解決資料的準確性,保證同一個臨界區的資料只能被一個執行緒進行操作,日常中使用到的並發場景也是很多的:

計數器:計數器結果不準確;

秒殺系統

:由於相同時間訪問量比較大,導致的超賣;
  • 用戶帳戶異常:同一時間支付導致的帳戶透支;
  • buffer 資料異常:更新buffer 導致的數據混亂。
  • 上面都是並發帶來的資料準確性的問題,決絕方案就是使用互斥鎖
  • ,也就是今天並發程式設計中的所要描述的Mutex 並發原語。
  • 實作機制

互斥鎖 Mutex 是為了避免並發競爭建立的並發控制機制,其中有個「臨界區」的概念。

在並發程式設計過程中,如果程式中一部分資源或變數會被並發存取或修改,為了避免並發存取導致資料的不準確,這部分程式需要率先被保護起來,之後操作,操作結束後去除保護,這部分被保護的程式就叫做

臨界區

使用互斥鎖,限定臨界區只能同時由一個執行緒持有
,若是臨界區此時被一個執行緒持有,那麼其他執行緒想進入到這個臨界區的時候,就會失敗或等待釋放鎖,持有此臨界區的執行緒退出,其他執行緒才有機會獲得這個臨界區。

go mutex 臨界區示意圖

#Mutex 是Go 語言中使用最廣泛的同步原語,也稱為並發原語,解決的是並發讀寫共享資源,避免出現資料競爭data race 問題

基本上使用

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

type Locker interface {
    Lock()
    Unlock()}func(m *Mutex)Lock()func(m *Mutex)Unlock()

當一個 goroutine 呼叫 Lock 方法取得到鎖後,其他 goroutine 會阻塞在 Lock 的呼叫上,直到目前取得到鎖的 goroutine 釋放鎖。

接下來是一個計數器的例子,是由100 個goroutine 對計數器進行累加操作,最後輸出結果:

package mainimport (
    "fmt"
    "sync")func main() {
    var mu sync.Mutex
    countNum := 0

    // 确认辅助变量是否都执行完成
    var wg sync.WaitGroup    // wg 添加数目要和 创建的协程数量保持一致
    wg.Add(100)
    for i := 0; i < 100; i++ {
        go func() {
            defer wg.Done()
            for j := 0; j < 1000; j++ {
                mu.Lock()
                countNum++
                mu.Unlock()
            }
        }()
    }
    wg.Wait()
    fmt.Printf("countNum: %d", countNum)}

##實際使用

很多時候Mutex 並不是單獨使用的,而是嵌套在Struct 中使用,作為結構體的一部分,如果嵌入的struct 有多個字段,我們一般會把Mutex 放在要控制的字段上面,然後使用空格把欄位分隔開來。

甚至可以把取得鎖定、釋放鎖定、計數加一的邏輯封裝成一個方法。
package mainimport (
    "fmt"
    "sync")// 线程安全的计数器type Counter struct {
    CounterType int
    Name        string

    mu    sync.Mutex
    count uint64}// 加一方法func (c *Counter) Incr() {
    c.mu.Lock()
    defer c.mu.Unlock()
    c.count++}// 取数值方法 线程也需要受保护func (c *Counter) Count() uint64 {
    c.mu.Lock()
    defer c.mu.Unlock()
    return c.count}func main() {
    // 定义一个计数器
    var counter Counter    var wg sync.WaitGroup
    wg.Add(100)

    for i := 0; i < 100; i++ {
        go func() {
            defer wg.Done()
            for j := 0; j < 1000; j++ {
                counter.Incr()
            }
        }()
    }
    wg.Wait()

    fmt.Printf("%d\n", counter.Count())}

思考問題

Q:你已經知道,如果Mutex 已經被一個goroutine 取得了鎖,其它等待中的goroutine 只能一直等待。那麼,等這個鎖釋放後,等待中的 goroutine 中哪一個會優先取得 Mutex 呢?

A:FIFO,先來先服務的策略,Go 的goroutine 調度中,會維護一個保障goroutine 運行的隊列,當獲取到鎖的goroutine 執行完臨界區的操作的時候,就會釋放鎖,在佇列中排在第一位置的goroutine 會拿到鎖進行臨界區的操作。                

                             

#######################################################################################################

以上是關於 Go 並發程式設計 Mutex的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:learnku.com。如有侵權,請聯絡admin@php.cn刪除