[英]Why does the goroutine not block for time.Sleep(duration)
[英]does Sleep in golang block other goroutine?
其實我正在嘗試做這樣的事情:
我有一個生產者和一個消費者,消費者每隔幾分鍾檢查一次並計算頻道中的事件。 但是當我嘗試在 go 操場上測試它時,我發現:
package main
import (
"fmt"
"time"
)
func main() {
c1 := make(chan string, 1)
quit := make(chan int)
go func() {
for i := 0; i < 10; i++ {
c1 <- "result 1"
}
quit <- 1
}()
count := 0
for stop := false; !stop; {
for bk := false; !bk; {
select {
case _, ok := <-c1:
if ok {
count++
}
default:
bk = true
fmt.Println("default")
}
}
select {
case <-quit:
fmt.Println("stop")
stop = true
default:
}
fmt.Println(count)
time.Sleep(time.Second / 10)
}
fmt.Println("over")
}
無論我睡多久,從 time.Second/10 到 time.Second*10,output 將是:
default
0
default
2
default
4
default
6
default
8
default
10
default
stop
10
over
為什么 goroutine 只能在通道中放置 2 個事件? 我想要類似的東西:
default
0
default
stop
10
over
問題是頻道大小,我只是從其他代碼中復制而不檢查......
這個 function:
go func() {
for i := 0; i < 10; i++ {
c1 <- "result 1"
}
quit <- 1
}()
總是——嗯,總是在它可以運行的時候——試圖將一個字符串放入通道c1
(直到它已經放入十個,無論如何)。
您制作了頻道c1
:
c1 := make(chan string, 1)
所以它有空間容納一個待處理的項目。 因此,如果通道為空,則循環放入一個項目,然后嘗試放入第二個項目。 如果此時通道已滿——不能保證它是滿的,但暫時假設它是滿的——這個 goroutine 現在暫停,等待有人將前一個項目從通道中拉出。
同時,在加或減幾納秒的時間——或者可能在其他 goroutine 塊1之前或之后——你正在運行另一段代碼。 (不能保證確實如此,但事實就是如此。)
for bk := false; !bk; {
select {
case _, ok := <-c1:
if ok {
count++
}
default:
bk = true
fmt.Println("default")
}
}
此代碼檢查通道中是否有任何內容。 因為匿名發件人將一件物品放入其中,所以頻道中確實有一些東西。 此代碼刪除一個項目,在通道中創建空間。 這會導致匿名發件人中的阻止發送現在運行一步。 不能保證它會這樣做,但事實上,它確實會這樣做 - 所以現在頻道中有另一個項目。
但是,在運行了這一步之后,匿名發件人現在會暫停幾納秒。 2您的循環返回頂部並檢查c1
中是否有項目。 有,所以你的循環接受它並計算它:你現在已經接受了另外兩個項目。 您的循環返回頂部並檢查c1
中是否還有其他項目。 匿名發件人仍在喘氣,或者類似的事情,並且沒有在通道中獲得第三個值 - 因此您的循環檢測到通道是空的並采用default
子句,這會在此處中斷您的循環。
運行main
的 goroutine 現在打印行default
,檢查它是否應該停止(否),並暫停幾分之一秒。 在所有這些過程中的某個時間——實際上,在暫停時——匿名發送者有機會運行,將一個項目放入通道,並在它試圖將第二個項目放入通道的點處阻塞。 這只需要幾十或幾百納秒。
Sleep
呼叫仍被阻止,您的匿名發件人也是如此,但喚醒將在幾分之一秒內發生。 當它這樣做時,您的主循環將返回頂部,以運行從c1
中讀取項目的內部!bk
循環。 您現在位於與上次相同的 state 中,因此您這次也將從c1
中讀取兩個項目。
這對程序運行的 rest 重復。
這里的幾個步驟不能保證以這種方式發生。 考慮到當前的實現以及您在單 CPU 虛擬機上運行所有這些的事實,它們只是碰巧實際上是這樣運行的。 如果這兩種情況中的任何一種發生變化——例如,如果你在多 CPU 系統上運行,或者實現被修改——你的程序的行為可能會改變。
1事實上,第一次通過時,主程序正在運行,匿名發件人尚未啟動,因此計數為零。 主 goroutine 在其Sleep
調用中阻塞,這允許匿名發送者在單個 CPU 上運行,為您第二次通過主例程做好准備。
2或者,在這種情況下,只要主 goroutine 有機會再次運行。 這具體取決於 Playground VM 的單 CPU 方面。
[D] oes 在 golang 中休眠會阻塞其他 goroutine?
不。
for stop := false; !stop; {
for bk := false; !bk; {
select {
case _, ok := <-c1:
// --------- add this ---------
time.Sleep(time.Second / 100)
if ok {
count++
}
default:
bk = true
fmt.Println("default")
}
}
select {
case <-quit:
fmt.Println("stop")
stop = true
default:
}
fmt.Println(count)
time.Sleep(time.Second / 10)
}
fmt.Println("over")
因為通道比“選擇默認”慢。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.