Rumah  >  Artikel  >  pembangunan bahagian belakang  >  Apakah yang menyebabkan goroutine saya tersekat dalam kod mutex berikut?

Apakah yang menyebabkan goroutine saya tersekat dalam kod mutex berikut?

王林
王林ke hadapan
2024-02-13 18:57:07651semak imbas

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

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.

Kandungan soalan

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

Solution

mylock boleh mengunci mutex global dan mutex individu. Ini menjadikan membuka kunci mustahil dalam beberapa kes:

  1. goroutine 1 memanggil mylock(2). Ini menyebabkan kunci tunggal l2 dikunci seperti yang dijangkakan.
  2. goroutine 2 memanggil mylock(2). Ia memperoleh kunci global dan kemudian menyekat menunggu l2 dikeluarkan. Semasa menunggu, kunci global terus dipegang.
  3. goroutine 1 memanggil myunlock(2). Ia disekat menunggu kunci global (dipegang oleh goroutine 2) dikeluarkan. Ini adalah jalan buntu.

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!

Kenyataan:
Artikel ini dikembalikan pada:stackoverflow.com. Jika ada pelanggaran, sila hubungi admin@php.cn Padam