3 回答

TA貢獻1898條經驗 獲得超8個贊
這個功能:
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,這會在此處中斷您的循環。
現在運行的 goroutine 會main打印該行default,檢查是否應該停止(否),并暫停幾分之一秒。在所有這一切過程中的某個時間(實際上是在暫停時),匿名發送者有機會運行,將一個項目放入通道中,并在嘗試將第二個項目放入通道中時阻塞。這只需要幾十或幾百納秒。
呼叫Sleep仍然被阻止,就像您的匿名發件人一樣,但喚醒將在幾分之一秒內發生。當它發生時,您的主循環將返回到頂部,以運行!bk從 中讀取項目的內部循環c1。您現在處于與上次相同的狀態,因此這次您還將閱讀兩項c1。
程序運行的其余部分會重復此操作。
這里的幾個步驟不能保證以這種方式發生??紤]到當前的實現以及您在單 CPU 虛擬機上運行所有這些的事實,它們實際上只是碰巧以這種方式運行。如果這兩種情況中的任何一個發生變化(例如,如果您在多 CPU 系統上運行,或者實現被修改),您的程序的行為可能會發生變化。
1事實上,第一次通過時,主例程正在運行,匿名發送者尚未啟動,因此計數為零。主 goroutine 會阻塞在其Sleep調用中,這允許匿名發送者在單個 CPU 上運行,為您第二次訪問主例程做好準備。
2或者,在本例中,只要主 Goroutine 給它再次運行的機會即可。這具體取決于 Playground VM 的單 CPU 方面。

TA貢獻1744條經驗 獲得超4個贊
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")
因為該通道比“選擇默認”慢。
- 3 回答
- 0 關注
- 284 瀏覽
添加回答
舉報