[英]Why goroutine leaks
我在第30頁閱讀了Twelve Go Best Practices和遭遇以及有趣的例子。
func sendMsg(msg, addr string) error {
conn, err := net.Dial("tcp", addr)
if err != nil {
return err
}
defer conn.Close()
_, err = fmt.Fprint(conn, msg)
return err
}
func broadcastMsg(msg string, addrs []string) error {
errc := make(chan error)
for _, addr := range addrs {
go func(addr string) {
errc <- sendMsg(msg, addr)
fmt.Println("done")
}(addr)
}
for _ = range addrs {
if err := <-errc; err != nil {
return err
}
}
return nil
}
func main() {
addr := []string{"localhost:8080", "http://google.com"}
err := broadcastMsg("hi", addr)
time.Sleep(time.Second)
if err != nil {
fmt.Println(err)
return
}
fmt.Println("everything went fine")
}
程序員提到,這恰好發生在上面的代碼中:
the goroutine is blocked on the chan write
the goroutine holds a reference to the chan
the chan will never be garbage collected
為什么goroutine在這里受阻? 主線程被阻塞,直到它從goroutine接收數據。 在繼續for循環之后。 不?
為什么errc chan永遠不會被垃圾收集? 因為我沒有關閉頻道,完成goroutine后?
我看到的一個問題是goroutines啟動后的broadcastMsg()
內部:
for _ = range addrs {
if err := <-errc; err != nil {
return err
}
}
如果一個非nil
error
是從接收errc
, broadcastMsg()
與該錯誤立即返回,並且不從通道,這意味着進一步夠程將永遠不會因為暢通接受任何最新值errc
是緩沖的。
可能的解決方法是使用緩沖通道,大到足以阻止任何goroutine,在這種情況下:
errc := make(chan error, len(addrs))
或者即使非nil
error
從信道接收,仍繼續盡可能多的夠程就可以發送接收多次:
var errRec error
for _ = range addrs {
if err := <-errc; err != nil {
if errRec == nil {
errRec = err
}
}
}
return errRec
或者如幻燈片#33上的鏈接談話中所述:使用“退出”通道來防止在broadcastMsg()
完成/返回后啟動的goroutine保持阻塞狀態。
您有兩個地址列表(localhost,google)。 對於這些中的每一個,您使用每個地址使用一個goroutine發送消息(hi)。 並且goroutine向errc通道發送錯誤(可能是nil)。
如果你向某個頻道發送內容,你還需要一些東西來讀取該頻道的值,否則它會阻塞(除非它是一個緩沖的頻道,但是一旦它們的緩沖區已滿,即使是緩沖的頻道也會阻塞)。
所以你的閱讀循環看起來像這樣:
for _ = range addrs {
if err := <-errc; err != nil {
return err
}
}
如果第一個地址返回的錯誤不是nil,則循環返回。 永遠不會從通道讀取后續錯誤值,因此它會阻塞。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.