首頁  >  文章  >  後端開發  >  是什麼導致我的 goroutine 在以下互斥體程式碼中陷入僵局?

是什麼導致我的 goroutine 在以下互斥體程式碼中陷入僵局?

王林
王林轉載
2024-02-13 18:57:07651瀏覽

是什么导致我的 goroutine 在以下互斥体代码中陷入僵局?

php小編新一在這裡給大家解答一個常見問題:「是什麼導致我的goroutine在以下互斥體程式碼中陷入僵局?」在並發程式設計中,使用互斥鎖(Mutex)是常見的解決共享資源競爭的方法之一。然而,如果程式碼中存在一些問題,可能會導致goroutine陷入僵局,無法繼續執行。接下來,我們將詳細討論可能導致該問題的原因,並給出解決方案。

問題內容

我試著保存一張鑰匙圖,每個鑰匙都有單獨的鎖。 在為特定密鑰建立鎖定時,我使用全域互斥體寫入映射。

完成為鑰匙建立鎖定後,我將使用新鎖並在完成工作後釋放。 目前我正在嘗試修改單一密鑰以測試我的程式碼。

這是程式碼:

// You can edit this code!
// Click here and start typing.
package main

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

var count int
var globalMutex *sync.RWMutex
var mutexes map[int]*sync.Mutex

func MyLock(index int) {
    fmt.Println("Aquiring Lock")
    globalMutex.Lock()
    defer globalMutex.Unlock()

    count++

    if mutexes == nil {
        mutexes = make(map[int]*sync.Mutex)
    }

    if _, ok := mutexes[index]; !ok {
        mutexes[index] = &sync.Mutex{}
    }

    fmt.Println("Aquiring 2nd Lock")
    mutexes[index].Lock()
    fmt.Println("Aquired Lock")
}

func MyUnlock(index int) {
    globalMutex.Lock()
    defer globalMutex.Unlock()
    mutexes[index].Unlock()
}

func main() {
    var wg sync.WaitGroup
    globalMutex = &sync.RWMutex{}
    wg.Add(500)
    for i := 0; i < 500; i++ {
        go func(i int) {
            defer wg.Done()

            MyLock(2)

            time.Sleep(1 * time.Second)
            fmt.Println(i)

            MyUnlock(2)
        }(i)
    }

    wg.Wait()
    fmt.Println(mutexes)
    fmt.Println(count)
}

我不確定為什麼它無法取得鎖。 遊樂場連結:https://go.dev/play/p/-co0xaxpuy0

解決方法

mylock 可鎖定全域互斥鎖和單一互斥鎖。這使得在某些情況下無法解鎖:

  1. goroutine 1 呼叫 mylock(2)。這會導致單一鎖 l2 按預期被鎖定。
  2. goroutine 2 呼叫 mylock(2)。它取得全域鎖,然後被阻塞等待 l2 被釋放。在等待期間,全域鎖定繼續保持。
  3. goroutine 1 呼叫 myunlock(2)。它被阻塞等待全域鎖(由 goroutine 2 持有)被釋放。這就是僵局。

要解決此問題,請在持有全域鎖時返回各個鎖,而不是(取消)鎖定它們。 myunlock 功能變得不必要:

func mylock(index int) *sync.mutex {
    globalmutex.lock()
    defer globalmutex.unlock()

    count++

    if mutexes == nil {
        mutexes = make(map[int]*sync.mutex)
    }

    mu := mutexes[index]
    if mu == nil {
        mu = &sync.mutex{}
        mutexes[index] = mu
    }

    return mu
}

func main() {
    var wg sync.waitgroup
    globalmutex = &sync.rwmutex{}
    wg.add(500)
    for i := 0; i < 500; i++ {
        go func(i int) {
            defer wg.done()

            mu := mylock(2)
            mu.lock()
            defer mu.unlock()

            time.sleep(1 * time.second)
            fmt.println(i)
        }(i)
    }

    wg.wait()
    fmt.println(mutexes)
    fmt.println(count)
}

為了提高效能,您可以先檢查單一鎖定是否存在,同時僅持有全域讀取鎖定(請注意,這會變更 count 所代表的內容):

func MyLock(index int) *sync.Mutex {
    globalMutex.RLock()
    mu := mutexes[index]
    globalMutex.RUnlock()

    if mu != nil {
        return mu
    }
    
    globalMutex.Lock()
    defer globalMutex.Unlock()

    count++

    if mutexes == nil {
        mutexes = make(map[int]*sync.Mutex)
    }

    mu = mutexes[index] // have to check again because
    if mu == nil {      // we briefly released globalMutex 
        mu = &sync.Mutex{}
        mutexes[index] = mu
    }

    return mu
}

以上是是什麼導致我的 goroutine 在以下互斥體程式碼中陷入僵局?的詳細內容。更多資訊請關注PHP中文網其他相關文章!

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