1 回答
TA貢獻1155條經驗 獲得超0個贊
無限循環(huán)本身并沒有錯。當循環(huán)退出條件需要太多命令才能輕松放入 for 條件時,我經常使用 for { ... } 構造。
根據(jù)我的$GOPATH/src/github.com/目錄,這顯然是一個非常不完整的樣本集,我看到了數(shù)百種超出我自己的這樣的用途。github.com/docker/docker僅此一項似乎就使用了 454 個這樣的無限循環(huán)。
不太合適的是在循環(huán)中創(chuàng)建一個只傳遞一個值的通道的想法。如果你的 goroutine 總是只返回一個值,那么這個返回值的存在就足以表明 goroutine 已經完成。盡可能重復使用通道,如果您想稍后發(fā)送更多數(shù)據(jù),請不要關閉它們。
顯然,在您的情況下,goroutine 無論如何都是毫無意義的,僅用于教育目的。但是考慮一下,如果你愿意的話:
package main
import (
"log"
)
func doStuff(datachan <-chan map[string]string, reschan chan<- int) {
for {
data, ok := <-datachan
if !ok {
log.Print("Channel closed.")
break
}
log.Printf("Data had %d length: %+v", len(data), data)
reschan<-len(data)
}
return
}
const workers = 3
func main() {
var datachan = make(chan map[string]string)
var reschan = make(chan int)
var inflight = 0
var inputs = []map[string]string {
map[string]string{ "hi": "world" },
map[string]string{ "bye": "space", "including": "moon" },
map[string]string{ "bye": "space", "including": "moon" },
map[string]string{ },
map[string]string{ },
}
// an inline funciton definition can change inflight within main()'s scope
processResults := func (res int) {
log.Printf("Main function got result %d", res)
inflight--
}
// start some workers
for i := 0; i < workers; i++{
go doStuff(datachan, reschan)
}
for _, data := range inputs {
//Select allows reading from reschan if datachan is not available for
// writing, thus freeing up a worker to read from datachan next loop
written := false
for written != true {
select {
case res := <-reschan:
processResults(res)
case datachan <- data:
inflight++
written = true
}
}
}
close(datachan)
for inflight > 0 {
processResults(<-reschan)
}
}
輸出:
2020/10/31 13:15:08 Data had 1 length: map[hi:world]
2020/10/31 13:15:08 Main function got result 1
2020/10/31 13:15:08 Data had 0 length: map[]
2020/10/31 13:15:08 Main function got result 0
2020/10/31 13:15:08 Data had 0 length: map[]
2020/10/31 13:15:08 Channel closed.
2020/10/31 13:15:08 Main function got result 0
2020/10/31 13:15:08 Data had 2 length: map[bye:space including:moon]
2020/10/31 13:15:08 Channel closed.
2020/10/31 13:15:08 Main function got result 2
2020/10/31 13:15:08 Data had 2 length: map[bye:space including:moon]
2020/10/31 13:15:08 Channel closed.
2020/10/31 13:15:08 Main function got result 2
for {在這里,我添加了一些結構來說明和的一些更常見的用法close(chan)。
我在工作goroutines 中使用了一個潛在的無限循環(huán),其中有 3 個(故意創(chuàng)建的比使用的多)。我數(shù)了數(shù)我給頻道寫了多少次,以確保我閱讀了每一個回復。當主 goroutine 結束時,所有其他 goroutine 都會被毫不客氣地殺死,所以我要確保讓它們完成。計算結果是一種簡單的方法。
我還演示了正確使用close(chan). 雖然在使用后關閉通道(例如您所做的)是不正確的,但通常沒有必要這樣做,因為在所有對它們的引用無論如何都消失后,打開的通道將被垃圾收集。( https://stackoverflow.com/questions/8593645/is-it-ok-to-leave-a-channel-open#:~:text=It's%20OK%20to%20leave%20a,that%20no%20more% 20data%20 關注。)
close(chan)通常用于告訴頻道閱讀器該頻道上沒有更多數(shù)據(jù)可用。
data, ok := <-datachan
第二個值,一個布爾值,將告訴我們是否讀取data或通道實際上已關閉和耗盡。所以這是確保我們已經處理所有通道的接收器部分。
因為我使用select,這段代碼可以處理inputs任意長度的,帶有一組靜態(tài)工作人員。這些通道都沒有緩沖 - 讀者必須在閱讀才能讓作者寫入。因此,在嘗試向該閱讀器發(fā)送另一個數(shù)據(jù)輸入之前,我需要確保從工作人員那里收到任何結果。使用select使這變得微不足道:操作在首先準備好的任何通道上成功(如果兩個通道都準備好,則隨機選擇一個選項 - 在這種情況下完美運行)。
for {,close(chan)和select, 總之,在向 goroutine worker bools 發(fā)送未知數(shù)量的輸入時,它們可以很好地協(xié)同工作。
一些最后的筆記。在現(xiàn)實世界中,通常會使用https://gobyexample.com/waitgroups而不是手動實現(xiàn)這一切。這個概念大體上是相同的,但它更少跟蹤事物并導致更清晰的代碼。我自己實現(xiàn)了它,所以概念很清楚。
最后,你會注意到在程序結束之前無法保證工作 goroutines 看到關閉的通道。實際上,從技術上講,可能不會從所有 goroutine 中記錄“關閉通道”消息。但是使用inflight計數(shù)器確保我得到了他們的結果,即使他們沒有機會觀察通道的關閉。當應用程序隨著時間的推移繼續(xù)運行多批工作人員時,關閉通道和退出工作人員更有意義 - 如果我們沒有通知他們關閉,但后來創(chuàng)建了更多工作人員,這將導致內存泄漏,就像那些工作人員一樣繼續(xù)等待永遠不會到來的輸入。或者,對多批請求使用同一組工作人員并不罕見。
- 1 回答
- 0 關注
- 195 瀏覽
添加回答
舉報
