Iris 這個在 go 語言上地表最快的網頁框架-執行序的鎖

多執行序問題的解決

在上一篇介紹了多執行序的好處以及撰寫方式,但是也提到了多執行序的問題,所以這篇針對多執行序遇到的race condition在 go 語言如何解決它來說明。

多執行序的鎖

在程式執行的過程中可能會做一個改變物件屬性的動作,然而在多執行序下會因為這個動作產生一個問題,就是後面執行的會把前面執行的結果給蓋掉。
所以通常的解決方式就是加一個鎖 mutex 讓要更改這物件的執行序排隊來更改,接下來就跟大家介紹如何撰寫這個鎖。

鎖如何撰寫

這時還是一樣請大家先看看Go Tour的範例

package main

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

// SafeCounter is safe to use concurrently.
type SafeCounter struct {
    v   map[string]int
    mux sync.Mutex
}

// Inc increments the counter for the given key.
func (c *SafeCounter) Inc(key string) {
    c.mux.Lock()
    // Lock so only one goroutine at a time can access the map c.v.
    c.v[key]++
    c.mux.Unlock()
}

// Value returns the current value of the counter for the given key.
func (c *SafeCounter) Value(key string) int {
    c.mux.Lock()
    // Lock so only one goroutine at a time can access the map c.v.
    defer c.mux.Unlock()
    return c.v[key]
}

func main() {
    c := SafeCounter{v: make(map[string]int)}
    for i := 0; i < 1000; i++ {
        go c.Inc("somekey")
    }

    time.Sleep(time.Second)
    fmt.Println(c.Value("somekey"))
}

相信有閱讀筆者前幾篇文章可以理解上面的程式在做甚麼,這邊針對 make 關鍵字加以說明。
這個 make 主要的用途就是產生一個記憶體空間給變數,所以簡單講上面的例子就是產生一個key是字串value是整數的map給變數 c 的屬性 v 。
接下來在下一個段落說明一下 mutex 的運作方式。

鎖的運作方式

搭配上面的例子,主要是在 Inc 跟 Value 這兩個方法,因為要避免race condition的問題所以不管在存取前都要加上一個鎖,限制只能有一個執行序來存取變數,而其他未取得鎖的直行序會等待鎖解開之後取得鎖在執行。
所以在新增之前要先呼叫 c.Mutex.Lock() 來取得鎖,並鎖住執行序來更新該變數,等到更新完了之後一定要記得呼叫 c.mux.Unlock() 來解鎖,不然就永遠沒有可以執行存取變數的權限了,而其他執行序也不能繼續執行下去。
因此在 Value 這方法也是需要取的鎖來取的變數的值,正因為需要回傳值,所以這邊用到了 defer 待回傳值之後會呼叫 c.mux.Unlock() 來解鎖。
其實也可以在前面先寫下 defer c.mux.Unlock() 來避免方法結束後沒有解鎖的窘境。

結論

這篇跟大家介紹多執行序如何利用 mutex 來解決race condition的問題,也針對相關語法做進一步說明,希望對大家的 go 多執行序編程能有些幫助。

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *