我試圖理解 Go 中的背景關系和通道,但我無法理解正在發生的事情。這是一些示例代碼。
package main
import (
"context"
"fmt"
"log"
"time"
"golang.org/x/time/rate"
)
func main() {
msgs := make(chan string)
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
defer cancel()
limiter := rate.NewLimiter(rate.Every(2*time.Second), 1)
go func(ctx context.Context, limiter *rate.Limiter) {
for {
limiter.Wait(context.Background())
select {
case <-ctx.Done():
log.Printf("finished!")
return
case msg := <-msgs:
log.Printf("receiving a message: %s", msg)
}
}
}(ctx, limiter)
defer close(msgs)
i := 0
for {
msgs <- fmt.Sprintf("sending message %d", i)
i
if i > 10 {
break
}
}
}
我得到的結果是不確定的。有時記錄器會列印出三條訊息,有時是五條。此外,程式每次都以死鎖結束:
2021/12/31 02:07:21 receiving a message: sending message 0
2021/12/31 02:07:23 receiving a message: sending message 1
2021/12/31 02:07:25 receiving a message: sending message 2
2021/12/31 02:07:27 receiving a message: sending message 3
2021/12/31 02:07:29 receiving a message: sending message 4
2021/12/31 02:07:29 finished!
fatal error: all goroutines are asleep - deadlock!
所以,我想我有幾個問題:
- 為什么我的 goroutine 不會在一秒鐘后簡單地結束?
- 為什么會出現死鎖?我怎樣才能避免這種性質的僵局?
uj5u.com熱心網友回復:
為什么我的 goroutine 不會在一秒鐘后簡單地結束?
雖然 goroutine 可能會在這里等待而不是select:
limiter.Wait(context.Background())
為什么會出現死鎖?我怎樣才能避免這種性質的僵局?
這是你的主要 goroutine 卡住了。它發生在這里:
msgs <- fmt.Sprintf("sending message %d", I)
沒有可以從 讀取的 goroutine msgs,所以它永遠等待。
這是使其作業的方法之一:
package main
import (
"context"
"fmt"
"log"
"time"
"golang.org/x/time/rate"
)
func main() {
msgs := make(chan string)
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
limiter := rate.NewLimiter(rate.Every(1*time.Second), 1)
go func() {
for {
limiter.Wait(context.Background())
select {
case <-ctx.Done():
log.Printf("finished!")
return
case msg := <-msgs:
log.Printf("receiving a message: %s", msg)
}
}
}()
defer close(msgs)
for i := 0; i < 100000; i {
select {
case msgs <- fmt.Sprintf("sending message %d", i):
case <-ctx.Done():
log.Printf("finished too!")
return
}
}
}
uj5u.com熱心網友回復:
使用 using 發送或接收訊息<-是一種阻塞操作。當背景關系超時結束時,goroutine 現在已經退出并且呼叫者無法繼續。
// Goroutine has finished, this call will never finish
msgs <- fmt.Sprintf("sending message %d", i)
此時程式已經造成死鎖。
至于非確定性,goroutine 和主執行背景關系并發運行。因為for沒有太多延遲,有兩個回圈,執行緒相互競爭。不能保證它們每次都以相同的方式執行。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/400769.html
