3 回答

TA貢獻1898條經(jīng)驗 獲得超8個贊
這個功能:
go func() {
for i := 0; i < 10; i++ {
c1 <- "result 1"
}
quit <- 1
}()
總是——嗯,總是在它可以運行的時候——嘗試將一個字符串放入通道c1(無論如何,直到它放入了十個字符串)。
您創(chuàng)建了頻道c1:
c1 := make(chan string, 1)
因此它有空間容納一件待處理的物品。因此,如果通道為空,循環(huán)將放入一項,然后嘗試放入第二項。如果此時通道已滿(不能保證已滿,但暫時假設已滿),此 Goroutine 現(xiàn)在暫停,等待有人將前一個項目從通道中拉出。
與此同時,在加上或減去幾納秒的同時——或者可能在其他 goroutine 塊1之前或之后——您正在運行另一段代碼。(不能保證情況確實如此,但事實確實如此。)
for bk := false; !bk; {
select {
case _, ok := <-c1:
if ok {
count++
}
default:
bk = true
fmt.Println("default")
}
}
此代碼檢查通道中是否有任何內(nèi)容。因為匿名發(fā)件人將一項內(nèi)容放入其中,所以該頻道中確實有一些內(nèi)容。此代碼刪除一項,在通道中創(chuàng)建空間。這會導致匿名發(fā)件人中被阻止的發(fā)送現(xiàn)在運行一步。無法保證它會這樣做,但事實上,它確實會這樣做 - 所以現(xiàn)在頻道中出現(xiàn)了另一個項目。
不過,運行完這一步后,匿名發(fā)件人現(xiàn)在暫停了幾納秒。2 您的循環(huán)返回到頂部并檢查 中是否有項目c1。有,所以你的循環(huán)接受它并計算它:你現(xiàn)在已經(jīng)接受了另外兩項。您的循環(huán)返回到頂部并檢查 中是否還有其他項目c1。匿名發(fā)件人仍在喘口氣,或者類似的事情,并且還沒有將第三個值輸入到通道中,因此您的循環(huán)檢測到通道為空并采用該子句default,這會在此處中斷您的循環(huán)。
現(xiàn)在運行的 goroutine 會main打印該行default,檢查是否應該停止(否),并暫停幾分之一秒。在所有這一切過程中的某個時間(實際上是在暫停時),匿名發(fā)送者有機會運行,將一個項目放入通道中,并在嘗試將第二個項目放入通道中時阻塞。這只需要幾十或幾百納秒。
呼叫Sleep仍然被阻止,就像您的匿名發(fā)件人一樣,但喚醒將在幾分之一秒內(nèi)發(fā)生。當它發(fā)生時,您的主循環(huán)將返回到頂部,以運行!bk從 中讀取項目的內(nèi)部循環(huán)c1。您現(xiàn)在處于與上次相同的狀態(tài),因此這次您還將閱讀兩項c1。
程序運行的其余部分會重復此操作。
這里的幾個步驟不能保證以這種方式發(fā)生??紤]到當前的實現(xiàn)以及您在單 CPU 虛擬機上運行所有這些的事實,它們實際上只是碰巧以這種方式運行。如果這兩種情況中的任何一個發(fā)生變化(例如,如果您在多 CPU 系統(tǒng)上運行,或者實現(xiàn)被修改),您的程序的行為可能會發(fā)生變化。
1事實上,第一次通過時,主例程正在運行,匿名發(fā)送者尚未啟動,因此計數(shù)為零。主 goroutine 會阻塞在其Sleep調(diào)用中,這允許匿名發(fā)送者在單個 CPU 上運行,為您第二次訪問主例程做好準備。
2或者,在本例中,只要主 Goroutine 給它再次運行的機會即可。這具體取決于 Playground VM 的單 CPU 方面。

TA貢獻1744條經(jīng)驗 獲得超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 關注
- 308 瀏覽
添加回答
舉報