[英]Where should I close channel on this specific example?
我正在寫一個簡單的 Go 管道,目標是獲取 url 和打印狀態。
在 fetchUrl 上,我需要關閉通道以通知 main,不會有數據傳入,因此釋放主 go 例程。 但是,我無法在循環后真正關閉 fetchurl function 上的頻道,因為它會為時過早。 我不想在應用程序中添加等待組,因為目前我的整個目標是了解渠道。
在 fetchurl function 上,稱為兩個的通道只是為了確保一次只有 2 個作業。
package main
import (
"fmt"
"net/http"
"os"
)
func gen(val []string) <-chan string {
out := make(chan string, len(val))
for _, val := range val {
out <- val
}
close(out)
return out
}
func fetchUrl(in <-chan string) <-chan string {
out := make(chan string)
two := make(chan struct{}, 2)
fmt.Println("blocked")
for url := range in {
two <- struct{}{}
go fetchWorker(url, two, out)
}
return out
}
func fetchWorker(url string, two chan struct{}, out chan string) {
res, err := http.Get("https://" + url)
if err != nil {
panic(err)
}
<-two
out <- fmt.Sprintf("[%d] %s\n", res.StatusCode, url)
}
func main() {
for val := range fetchUrl(gen(os.Args[1:])) {
fmt.Println(val)
}
}
在將每個結果寫入其中后,您需要關閉out
通道。 最簡單的方法是當所有的工作 goroutine 都退出時,而最簡單的方法是使用sync.WaitGroup
。 (在 Go 中,通道和 goroutine 是非常密切相關的概念,所以 goroutine 管理是使用通道的一部分。)
在現有代碼中,您可以將其綁定到您的fetchUrl
function:
var wg sync.WaitGroup
for url := range in {
two <- struct{}{}
wg.Add(1)
go func() {
defer wg.Done()
fetchWorker(url, two, out)
}()
}
wg.Wait()
close(out)
編寫代碼時遇到的另一個結構性問題是gen
和fetchUrl
都創建通道,運行所有應該寫入通道的代碼,並且只有在這些編寫者完成后才返回通道; 由於在 function 返回之前無法從通道讀取任何內容,這將導致死鎖。 您可以通過在頂層創建所有通道並將它們傳遞給生成器函數來解決此問題。
如果您想要兩個工作人員從同一個 URL 隊列中讀取,標准模式是啟動兩個從同一個通道讀取和寫入的 goroutine。 例如,您可以將fetchWorker
重寫為
func fetchWorker(urls <-chan string, out chan<- string) {
for url := range urls {
res, err := http.Get("https://" + url)
if err != nil {
panic(err)
}
out <- fmt.Sprintf("[%d] %s\n", res.StatusCode, url)
}
}
在頂層,創建通道、創建工人、提供輸入並使用 output。
func main() {
urls := make(chan string)
out := make(chan string)
// Launch a goroutine to feed data into urls, then
// close(urls), then stop
go gen(os.Args[1:], urls)
// Launch worker goroutines
workerCount := 2
var wg sync.WaitGroup
for i := 0; i < workerCount; i++ {
wg.Add(1)
go func() {
defer wg.Done()
fetchWorker(urls, out)
}()
}
// Launch a dedicated goroutine to close the channel
go func() {
wg.Wait()
close(out)
}()
// Read the results
for result := range(out) {
fmt.Println(result)
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.