sync.WaitGroup
ialah struktur data yang sangat biasa digunakan dalam persekitaran serentak, digunakan untuk menunggu tamatnya semua coroutine menulis kod, saya hanya mengikuti contoh, dan saya tidak perlu mendalami penggunaannya. Beberapa hari yang lalu, saya tertanya-tanya sama ada saya boleh melaksanakan fungsi Add()
dalam coroutine Jawapannya adalah tidak.
Perangkap terletak pada urutan panggilan bagi tiga fungsi WaitGroup. Mari kita semak dahulu fungsi tiga fungsi:
-
Add(delta int)
: Tambahkan delta ke kaunter, contohnya, naikkan 1 apabila memulakan coroutine. -
Done()
: Dilaksanakan sebelum coroutine keluar, kurangkan kaunter sebanyak 1. -
Wait()
: Kaunter menunggu yang menyekat ialah 0.
Lakukan ujian
Atur cara berikut mencipta bapa coroutine, dan kemudian coroutine bapa mencipta 10 sub-coroutine Fungsi utama menunggu semua coroutine selesai dan kemudian keluar. Lihat Lihat jika terdapat apa-apa yang salah dengan kod di bawah?
package main import ( "fmt" "sync" ) func father(wg *sync.WaitGroup) { wg.Add(1) defer wg.Done() fmt.Printf("father\n") for i := 0; i < 10; i++ { go child(wg, i) } } func child(wg *sync.WaitGroup, id int) { wg.Add(1) defer wg.Done() fmt.Printf("child [%d]\n", id) } func main() { var wg sync.WaitGroup go father(&wg) wg.Wait() log.Printf("main: father and all chindren exit") }
Adakah anda menemui masalahnya? Jika anda tidak melihat hasil berjalan berikut: fungsi utama mula tamat sebelum sub-coroutine tamat.
father main: father and all chindren exit child [9] child [0] child [4] child [7] child [8]
Analisis Perangkap
Sebab bagi masalah di atas ialah fungsi Add()
dilaksanakan dalam coroutine selepas coroutine dicipta, dan pada masa ini fungsi Wait()
mungkin sudah dilaksanakan, malah fungsi Wait()
dilaksanakan sebelum semua Add()
dilaksanakan Apabila Wait()
dilaksanakan, ia serta-merta memenuhi pembilang WaitGroup 0, Tunggu tamat dan program utama. keluar, menyebabkan semua sub-coroutine Sebelum ia keluar sepenuhnya, fungsi utama tamat.
Pendekatan yang betul
Fungsi Tambah mesti dilaksanakan sebelum fungsi Tunggu dilaksanakan Ini dicadangkan dalam dokumentasi fungsi Tambah: Perhatikan bahawa panggilan dengan delta positif yang berlaku apabila pembilang adalah sifar mesti berlaku sebelum Tunggu..
Bagaimana untuk memastikan bahawa fungsi Tambah mesti dilaksanakan sebelum fungsi Tunggu? Dalam kes coroutine, kami tidak boleh meramalkan sama ada masa pelaksanaan kod dalam coroutine lebih awal daripada masa pelaksanaan fungsi Tunggu Walau bagaimanapun, kami boleh memastikan bahawa dengan melaksanakan fungsi Tambah sebelum menetapkan coroutine dan kemudian melaksanakan Tunggu. fungsi.
Berikut ialah program yang diubah suai dan hasil output.
package main import ( "fmt" "sync" ) func father(wg *sync.WaitGroup) { defer wg.Done() fmt.Printf("father\n") for i := 0; i < 10; i++ { wg.Add(1) go child(wg, i) } } func child(wg *sync.WaitGroup, id int) { defer wg.Done() fmt.Printf("child [%d]\n", id) } func main() { var wg sync.WaitGroup wg.Add(1) go father(&wg) wg.Wait() fmt.Println("main: father and all chindren exit") }
father child [9] child [7] child [8] child [1] child [4] child [5] child [2] child [6] child [0] child [3] main: father and all chindren exit