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

Apakah yang menyebabkan goroutine saya tersekat dalam kod mutex berikut?

王林
Lepaskan: 2024-02-13 18:57:07
ke hadapan
716 orang telah melayarinya

是什么导致我的 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)
}
Salin selepas log masuk

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)
}
Salin selepas log masuk

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
}
Salin selepas log masuk

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!

sumber:stackoverflow.com
Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn
Tutorial Popular
Lagi>
Muat turun terkini
Lagi>
kesan web
Kod sumber laman web
Bahan laman web
Templat hujung hadapan