Rumah >pembangunan bahagian belakang >Golang >Mengapakah menghantar WaitGroup mengikut nilai dalam Go membawa kepada kebuntuan, dan bagaimanakah ia boleh diselesaikan?

Mengapakah menghantar WaitGroup mengikut nilai dalam Go membawa kepada kebuntuan, dan bagaimanakah ia boleh diselesaikan?

Linda Hamilton
Linda Hamiltonasal
2024-10-28 18:56:29618semak imbas

Why does passing a WaitGroup by value in Go lead to a deadlock, and how can it be resolved?

Kebuntuan dengan Saluran Go: Isu Skop Pembolehubah

Dalam program Golang, saluran memudahkan komunikasi antara goroutine. Walau bagaimanapun, penyalahgunaan saluran boleh menyebabkan kebuntuan, seperti yang ditunjukkan dalam kod di bawah:


pakej utama

import (

"fmt"
"sync"

)

func push(c chan int, wg sync.WaitGroup) {

for i := 0; i < 5; i++ {
    c <- i
}
wg.Done()

}

func pull(c chan int, wg sync.WaitGroup) {

for i := 0; i < 5; i++ {
    result, ok := <-c
    fmt.Println(result, ok)
}
wg.Done()

}

func main() {

var wg sync.WaitGroup
wg.Add(2)
c := make(chan int)

go push(c, wg)
go pull(c, wg)

wg.Wait() // Block the main thread until goroutines complete

}

Apabila menjalankan program ini, anda mungkin menghadapi ralat berikut:

fatal error: all goroutines are asleep - deadlock!

Untuk memahami sebab kebuntuan ini berlaku, mari kita teliti kod:

  • utama mencipta WaitGroup, saluran c dan goroutine untuk tolak dan tarik operasi.
  • Fungsi tolak dan tarik menggunakan WaitGroup untuk menyegerakkan pelaksanaannya.
  • Fungsi tolak menghantar nilai ke c dalam gelung dan menandakan siapnya dengan menggunakan wg.Done().
  • Fungsi tarik menerima nilai daripada c dan mencetaknya. Ia juga menandakan siap dengan wg.Done().

Masalahnya terletak pada cara WaitGroup dihantar ke goroutine. Apabila nilai diluluskan tanpa ampersand (&), ia diluluskan oleh nilai dan bukan dengan rujukan. Dalam kes ini, salinan WaitGroup dibuat untuk setiap goroutine.

Akibatnya, apabila setiap goroutine memanggil wg.Done(), ia mengubah suai salinan tempatan WaitGroup. Memandangkan utas utama menunggu sehingga wg menunjukkan bahawa semua goroutine telah selesai, ia menunggu selama-lamanya kerana kedua-dua goroutine tidak mengemas kini WaitGroup yang asal. Ini membawa kepada kebuntuan.

Untuk menyelesaikan isu ini, kita perlu lulus WaitGroup melalui rujukan. Ini memastikan bahawa kedua-dua goroutine mengubah suai contoh WaitGroup yang sama dan menandakan penyempurnaannya kepada urutan utama dengan betul.

Berikut ialah versi semakan kod dengan pembetulan:


pakej utama

import (

"fmt"
"sync"

)

func push(c chan int, wg *sync.WaitGroup) {

for i := 0; i < 5; i++ {
    c <- i
}
wg.Done()

}

func pull(c chan int, wg *sync.WaitGroup) {

for i := 0; i < 5; i++ {
    result, ok := <-c
    fmt.Println(result, ok)
}
wg.Done()

}

func main() {

var wg sync.WaitGroup
wg.Add(2)
c := make(chan int)

go push(c, &wg) // Pass the WaitGroup by reference using the ampersand
go pull(c, &wg) // Pass the WaitGroup by reference using the ampersand

wg.Wait()

}

Dengan menghantar WaitGroup melalui rujukan, kami memastikan bahawa urutan utama boleh menentukan dengan betul apabila kedua-dua gorout telah menyelesaikan tugas mereka, sekali gus mengelakkan kebuntuan.

Atas ialah kandungan terperinci Mengapakah menghantar WaitGroup mengikut nilai dalam Go membawa kepada kebuntuan, dan bagaimanakah ia boleh diselesaikan?. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
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