golang sync.WaitGroup never completes
我有以下代码,它获取 URL 列表,然后有条件地下载文件并将其保存到文件系统。文件被同时获取,主 goroutine 等待所有文件被获取。但是,在完成所有请求后,程序永远不会退出(并且没有错误)。
我认为正在发生的事情是,不知何故,
我明显做错了什么吗?我将如何检查
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 | package main import ( "fmt" "io" "io/ioutil" "net/http" "os" "strings" "sync" ) func main() { links := parseLinks() var wg sync.WaitGroup for _, url := range links { if isExcelDocument(url) { wg.Add(1) go downloadFromURL(url, wg) } else { fmt.Printf("Skipping: %v \ ", url) } } wg.Wait() } func downloadFromURL(url string, wg sync.WaitGroup) error { tokens := strings.Split(url,"/") fileName := tokens[len(tokens)-1] fmt.Printf("Downloading %v to %v \ ", url, fileName) content, err := os.Create("temp_docs/" + fileName) if err != nil { fmt.Printf("Error while creating %v because of %v", fileName, err) return err } resp, err := http.Get(url) if err != nil { fmt.Printf("Could not fetch %v because %v", url, err) return err } defer resp.Body.Close() _, err = io.Copy(content, resp.Body) if err != nil { fmt.Printf("Error while saving %v from %v", fileName, url) return err } fmt.Printf("Download complete for %v \ ", fileName) defer wg.Done() return nil } func isExcelDocument(url string) bool { return strings.HasSuffix(url,".xlsx") || strings.HasSuffix(url,".xls") } func parseLinks() []string { linksData, err := ioutil.ReadFile("links.txt") if err != nil { fmt.Printf("Trouble reading file: %v", err) } links := strings.Split(string(linksData),",") return links } |
这段代码有两个问题。首先,您必须将指向 WaitGroup 的指针传递给
见:
1 2 3 4 5 | func main() { ... go downloadFromURL(url, &wg) ... } |
其次,
1 2 3 4 | func downloadFromURL(url string, wg *sync.WaitGroup) error { defer wg.Done() ... } |
Go 中的参数总是按值传递。当参数可能被修改时使用指针。此外,请确保始终执行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 | package main import ( "fmt" "io" "io/ioutil" "net/http" "os" "strings" "sync" ) func main() { links := parseLinks() wg := new(sync.WaitGroup) for _, url := range links { if isExcelDocument(url) { wg.Add(1) go downloadFromURL(url, wg) } else { fmt.Printf("Skipping: %v \ ", url) } } wg.Wait() } func downloadFromURL(url string, wg *sync.WaitGroup) error { defer wg.Done() tokens := strings.Split(url,"/") fileName := tokens[len(tokens)-1] fmt.Printf("Downloading %v to %v \ ", url, fileName) content, err := os.Create("temp_docs/" + fileName) if err != nil { fmt.Printf("Error while creating %v because of %v", fileName, err) return err } resp, err := http.Get(url) if err != nil { fmt.Printf("Could not fetch %v because %v", url, err) return err } defer resp.Body.Close() _, err = io.Copy(content, resp.Body) if err != nil { fmt.Printf("Error while saving %v from %v", fileName, url) return err } fmt.Printf("Download complete for %v \ ", fileName) return nil } func isExcelDocument(url string) bool { return strings.HasSuffix(url,".xlsx") || strings.HasSuffix(url,".xls") } func parseLinks() []string { linksData, err := ioutil.ReadFile("links.txt") if err != nil { fmt.Printf("Trouble reading file: %v", err) } links := strings.Split(string(linksData),",") return links } |
正如@Bartosz 提到的,您需要传递对您的
的重要性方面做得很好
我喜欢
所以我想出了这个通用函数来为我解决这个问题:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | // Parallelize parallelizes the function calls func Parallelize(functions ...func()) { var waitGroup sync.WaitGroup waitGroup.Add(len(functions)) defer waitGroup.Wait() for _, function := range functions { go func(copy func()) { defer waitGroup.Done() copy() }(function) } } |
所以你的例子可以这样解决:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | func main() { links := parseLinks() functions := []func(){} for _, url := range links { if isExcelDocument(url) { function := func(url string){ return func() { downloadFromURL(url) } }(url) functions = append(functions, function) } else { fmt.Printf("Skipping: %v \ ", url) } } Parallelize(functions...) } func downloadFromURL(url string) { ... } |
如果你想使用它,你可以在这里找到它 https://github.com/shomali11/util