php editor Shinichi is here to answer a common question: "What causes my goroutine to get stuck in the following mutex code?" In concurrent programming, use Mutex lock (Mutex) is one of the common methods to solve competition for shared resources. However, if there are some problems in the code, it may cause the goroutine to become deadlocked and unable to continue execution. Next, we will discuss in detail the possible causes of this problem and give solutions.
I am trying to save a map of keys with separate locks for each key. When creating a lock for a specific key, I use a global mutex to write to the map.
After I finish creating the lock for the key, I will use the new lock and release it when the work is done. Currently I'm trying to modify a single key to test my code.
This is the code:
// 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) }
I'm not sure why it can't acquire the lock. Playground link: https://go.dev/play/p/-co0xaxpuy0
mylock can lock global mutexes and individual mutexes. This makes unlocking impossible in some cases:
To resolve this issue, return the individual locks while the global lock is held instead of (un)locking them. myunlock function becomes unnecessary:
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) }
To improve performance, you can first check if a single lock exists, while only holding a global read lock (note that this changes what count
represents):
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 }
The above is the detailed content of What causes my goroutine to get stuck in the following mutex code?. For more information, please follow other related articles on the PHP Chinese website!