Go中sync.Mutex 加鎖失效的問題解決
我先聲明一下,并不是真的加鎖失效,而是我之前的理解有誤,導致看起來像是加鎖失效一樣。于是乎記錄一下,加深一下印象。
我之前有個理解誤區(qū)(不知道大家有沒有,有的話趕緊糾正一下——其實也是因為我這塊的知識掌握不牢固導致的):覺得只要是加鎖后,在我主動調用解鎖之前,這個塊范圍內的變量一定不會被其他地方修改。后來驗證發(fā)現(xiàn),我大錯特錯了。
起因
最近在學習 sync.Mutex 加鎖時,寫了下面一段代碼進行練習。
package main import ( "fmt" "sync" "time" ) type Info struct { mu sync.Mutex Value string } func Update(info *Info) { fmt.Printf("%s: before update. Value: %s\n", time.Now().Format(timeFormat), info.Value) info.mu.Lock() defer info.mu.Unlock() time.Sleep(2 * time.Second) fmt.Printf("%s: in update. Value: %s\n", time.Now().Format(timeFormat), info.Value) info.Value = "update" fmt.Printf("%s: after update. Value: %s\n", time.Now().Format(timeFormat), info.Value) } const timeFormat = "2006-01-02 15:04:05" func main() { fmt.Printf("%s: main start\n", time.Now().Format(timeFormat)) info := &Info{} var wg sync.WaitGroup wg.Add(1) go func() { defer wg.Done() Update(info) }() time.Sleep(time.Second) info.Value = "main" fmt.Printf("%s: in main. Value: %s\n", time.Now().Format(timeFormat), info.Value) wg.Wait() }
按照我原先上面的理解,Update() 函數(shù)當中,before update 和 in update 中對應結構體的值應該是不會變的(畢竟我加了鎖)。然而從運行結果發(fā)現(xiàn),Update() 函數(shù)執(zhí)行期間,結構體變量的 Value 竟然還是被外部主線程修改了。
分析
那么為什么會這樣呢?明明 Update() 里邊已經(jīng)添加了鎖,為什么執(zhí)行期間還是會被其他地方修改呢?
最后發(fā)現(xiàn),究其原因,還是在于主線程修改變量的值的時候,沒有先判斷鎖 mu 是否已經(jīng)釋放,就直接進行了修改操作。
主線程中加上獲取鎖的操作后,會先判斷當前鎖是否被釋放,如果沒被釋放,就會一直進行等待直到鎖釋放后才繼續(xù)執(zhí)行后面的操作。
輸出結果也和預期保持一致 。
2024-04-16 23:59:13: main start
2024-04-16 23:59:13: before update. Value:
2024-04-16 23:59:15: in update. Value:
2024-04-16 23:59:15: after update. Value: update
2024-04-16 23:59:15: in main. Value: main
正常來說,鎖是要配合多 goroutine 來使用的, 對于單線程來說,由于沒有其他線程進行資源競爭,加鎖的意義不大;對于多 goroutine 而言,對于獲取和釋放鎖的時機,應該由應用程序合理控制。關于鎖的使用,還有一些其他注意事項,這塊也一并寫一下。
- 在一個 goroutine 獲得 Mutex 后,其他 goroutine 只能等到這個 goroutine 釋放該 Mutex
- 使用 Lock() 加鎖后,不能再繼續(xù)對其加鎖,直到利用 Unlock() 解鎖后才能再加鎖
- 在 Lock() 之前使用 Unlock() 會導致 panic 異常
- 已經(jīng)鎖定的 Mutex 并不與特定的 goroutine 相關聯(lián),這樣可以利用一個 goroutine 對其加鎖,再利用其他 goroutine 對其解鎖
- 在同一個 goroutine 中的 Mutex 解鎖之前再次進行加鎖,會導致死鎖
- 適用于讀寫不確定,并且只有一個讀或者寫的場景
緩沖通道實現(xiàn)互斥邏輯
當然,我們還可以通過緩沖為1的通道實現(xiàn)互斥鎖的邏輯。
package main import ( "fmt" "sync" "time" ) type Info struct { Value string } func Update(info *Info, sem chan bool) { fmt.Printf("%s: before update. Value: %s\n", time.Now().Format(timeFormat), info.Value) sem <- true defer func() { <- sem }() time.Sleep(2 * time.Second) fmt.Printf("%s: in update. Value: %s\n", time.Now().Format(timeFormat), info.Value) info.Value = "update" fmt.Printf("%s: after update. Value: %s\n", time.Now().Format(timeFormat), info.Value) } const timeFormat = "2006-01-02 15:04:05" func main() { sem := make(chan bool, 1) fmt.Printf("%s: main start\n", time.Now().Format(timeFormat)) info := &Info{} var wg sync.WaitGroup wg.Add(1) go func() { defer wg.Done() Update(info, sem) }() time.Sleep(time.Second) sem <- true info.Value = "main" fmt.Printf("%s: in main. Value: %s\n", time.Now().Format(timeFormat), info.Value) <- sem wg.Wait() }
2024-04-17 00:26:25: main start
2024-04-17 00:26:25: before update. Value:
2024-04-17 00:26:26: in main. Value: main
2024-04-17 00:26:27: in update. Value: main
2024-04-17 00:26:27: after update. Value: update
到此這篇關于Go中sync.Mutex 加鎖失效的問題解決的文章就介紹到這了,更多相關Go sync.Mutex 加鎖失效內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
golang復用http.request.body的方法示例
這篇文章主要給大家介紹了關于golang復用http.request.body的相關資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2018-10-10Golang實現(xiàn)自己的Redis(有序集合跳表)實例探究
這篇文章主要為大家介紹了Golang實現(xiàn)自己的Redis(有序集合跳表)實例探究,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2024-01-01