[英]Best way of using sync.WaitGroup with external function
我有以下代碼的一些問題:
package main
import (
"fmt"
"sync"
)
// This program should go to 11, but sometimes it only prints 1 to 10.
func main() {
ch := make(chan int)
var wg sync.WaitGroup
wg.Add(2)
go Print(ch, wg) //
go func(){
for i := 1; i <= 11; i++ {
ch <- i
}
close(ch)
defer wg.Done()
}()
wg.Wait() //deadlock here
}
// Print prints all numbers sent on the channel.
// The function returns when the channel is closed.
func Print(ch <-chan int, wg sync.WaitGroup) {
for n := range ch { // reads from channel until it's closed
fmt.Println(n)
}
defer wg.Done()
}
我在指定的地方陷入僵局。 我試過設置wg.Add(1)
而不是2,它解決了我的問題。 我的信念是,我沒有成功發送頻道作為Printer
功能的參數。 有沒有辦法做到這一點? 否則,我的問題的解決方案是將go Print(ch, wg)
行替換為:
go func() {
Print(ch)
defer wg.Done()
}
並將Printer
功能更改為:
func Print(ch <-chan int) {
for n := range ch { // reads from channel until it's closed
fmt.Println(n)
}
}
什么是最好的解決方案?
好吧,首先你的實際錯誤是你給了Print
方法一個sync.WaitGroup
的副本,所以它不會在你Wait()
那個上調用Done()
方法。
試試這個:
package main
import (
"fmt"
"sync"
)
func main() {
ch := make(chan int)
var wg sync.WaitGroup
wg.Add(2)
go Print(ch, &wg)
go func() {
for i := 1; i <= 11; i++ {
ch <- i
}
close(ch)
defer wg.Done()
}()
wg.Wait() //deadlock here
}
func Print(ch <-chan int, wg *sync.WaitGroup) {
for n := range ch { // reads from channel until it's closed
fmt.Println(n)
}
defer wg.Done()
}
現在,更改Print
方法以刪除它的WaitGroup
通常是一個好主意:該方法不需要知道有什么東西在等待它完成它的工作。
我同意@ Elwinar的解決方案,即代碼中的主要問題是將Waitgroup
的副本傳遞給Print
函數。
這意味着wg.Done()
在你在main
定義的wg
副本上運行。 因此, main
wg
無法降低,因此當你在main中wg.Wait()
時會發生死鎖。
既然你也在詢問最佳實踐,我可以給你一些我自己的建議:
不要在Print
刪除defer wg.Done()
。 由於你的main中的goroutine是發送者,而print是接收者, wg.Done()
在接收器例程中刪除wg.Done()
將導致未完成的接收者。 這是因為只有您的發件人與您的主郵件同步,因此在您的發件人完成后,您的主郵件已完成,但接收器可能仍在工作。 我的觀點是: 在主程序結束后,不要留下一些懸垂的goroutines。 關閉它們或等待它們。
記得到處都做恐慌,特別是匿名的goroutine。 我已經看到很多golang程序員忘記在goroutines中進行恐慌恢復,即使他們記得將恢復恢復到正常功能中。 當您希望代碼在發生意外情況時正常運行或至少優雅地運行時,這一點至關重要。
使用defer
每一個重要的電話之前,像sync
相關的電話, 一開始 ,因為你不知道在哪里的代碼可能會斷裂。 假設你在wg.Done()
之前刪除了defer
,並且在你的例子中你的匿名goroutine發生恐慌。 如果你沒有恐慌恢復,它會恐慌。 但是,如果你有恐慌恢復會發生什么? 現在一切都很好嗎? 不會。因為你的wg.Done()
因為恐慌而被跳過,所以你會在wg.Wait()
遇到死鎖! 但是,通過使用defer
,即使發生恐慌,也會在結束時執行此wg.Done()
。 此外,在close
之前推遲也很重要,因為其結果也會影響通信。
所以這里是根據我上面提到的點修改的代碼:
package main
import (
"fmt"
"sync"
)
func main() {
ch := make(chan int)
var wg sync.WaitGroup
wg.Add(2)
go Print(ch, &wg)
go func() {
defer func() {
if r := recover(); r != nil {
println("panic:" + r.(string))
}
}()
defer func() {
wg.Done()
}()
for i := 1; i <= 11; i++ {
ch <- i
if i == 7 {
panic("ahaha")
}
}
println("sender done")
close(ch)
}()
wg.Wait()
}
func Print(ch <-chan int, wg *sync.WaitGroup) {
defer func() {
if r := recover(); r != nil {
println("panic:" + r.(string))
}
}()
defer wg.Done()
for n := range ch {
fmt.Println(n)
}
println("print done")
}
希望能幫助到你 :)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.