Editor PHP Xinyi berada di sini untuk menjawab soalan biasa: "Apakah yang menyebabkan goroutine saya tersekat dalam kod mutex berikut Dalam pengaturcaraan serentak, penggunaan kunci mutex (Mutex) adalah perkara biasa Salah satu cara untuk menyelesaikannya?" persaingan untuk berkongsi sumber. Walau bagaimanapun, jika terdapat beberapa masalah dalam kod, ia boleh menyebabkan goroutine menjadi buntu dan tidak dapat meneruskan pelaksanaan. Seterusnya, kami akan membincangkan secara terperinci kemungkinan punca masalah ini dan memberi penyelesaian.
Saya cuba menyimpan peta kunci, setiap kunci mempunyai kunci yang berasingan. Apabila membuat kunci untuk kunci tertentu, saya menggunakan mutex global untuk menulis pada peta.
Selepas saya selesai mencipta kunci untuk kunci, saya akan menggunakan kunci baharu dan melepaskannya apabila kerja selesai. Pada masa ini saya cuba mengubah suai satu kunci untuk menguji kod saya.
Ini kodnya:
// 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) }
Saya tidak pasti mengapa ia tidak dapat memperoleh kunci. Pautan taman permainan: https://go.dev/play/p/-co0xaxpuy0
mylock boleh mengunci mutex global dan mutex individu. Ini menjadikan membuka kunci mustahil dalam beberapa kes:
Untuk membetulkannya, kembalikan kunci individu sambil memegang kunci global dan bukannya (nyah) kuncinya. fungsi myunlock menjadi tidak diperlukan:
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) }
Untuk meningkatkan prestasi, anda boleh menyemak dahulu sama ada satu kunci wujud, sambil hanya memegang kunci baca global (perhatikan bahawa ini mengubah apa yang diwakili 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 }
Atas ialah kandungan terperinci Apakah yang menyebabkan goroutine saya tersekat dalam kod mutex berikut?. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!