Concurrency Golang并发下载死锁

Concurrency Golang并发下载死锁,concurrency,go,Concurrency,Go,我想在go中并行下载文件,但我的代码永远不存在: package main import ( "fmt" "io" "net/http" "os" "path/filepath" "sync" ) func download_file(file_path string, wg sync.WaitGroup) { defer wg.Done() resp, _ := http.Get(file_path) defer re

我想在go中并行下载文件,但我的代码永远不存在:

package main

import (
    "fmt"
    "io"
    "net/http"
    "os"
    "path/filepath"
    "sync"
)

func download_file(file_path string, wg sync.WaitGroup) {
    defer wg.Done()
    resp, _ := http.Get(file_path)
    defer resp.Body.Close()
    filename := filepath.Base(file_path)
    file, _ := os.Create(filename)
    defer file.Close()

    size, _ := io.Copy(file, resp.Body)
    fmt.Println(filename, size, resp.Status)
}

func main() {
    var wg sync.WaitGroup

    file_list := []string{
        "http://i.imgur.com/dxGb2uZ.jpg",
        "http://i.imgur.com/RSU6NxX.jpg",
        "http://i.imgur.com/hUWgS2S.jpg",
        "http://i.imgur.com/U8kaix0.jpg",
        "http://i.imgur.com/w3cEYpY.jpg",
        "http://i.imgur.com/ooSCD9T.jpg"}
    fmt.Println(len(file_list))
    for _, url := range file_list {
        wg.Add(1)
        fmt.Println(wg)
        go download_file(url, wg)

    }
    wg.Wait()
}
原因是什么?我看了这里,但没有找到解决办法。
调试此类代码的最佳方法是什么?

正如Tim Cooper所说,您需要将WaitGroup作为指针传递。如果您在代码上运行go vet工具,它将向您发出以下警告:

$ go vet ex.go
ex.go:12: download_file passes Lock by value: sync.WaitGroup contains sync.Mutex
exit status 1
我建议您在保存文件时使用一个编辑器来完成这项工作。例如原子

至于代码,我认为您应该这样重新构造它:

package main

import (
    "fmt"
    "io"
    "net/http"
    "os"
    "path/filepath"
    "sync"
)

func downloadFile(filePath string) error {
    resp, err := http.Get(filePath)
    if err != nil {
        return err
    }
    defer resp.Body.Close()

    name := filepath.Base(filePath)

    file, err := os.Create(name)
    if err != nil {
        return err
    }
    defer file.Close()

    size, err := io.Copy(file, resp.Body)
    if err != nil {
        return err
    }
    fmt.Println(name, size, resp.Status)
    return nil
}

func main() {
    var wg sync.WaitGroup

    fileList := []string{
        "http://i.imgur.com/dxGb2uZ.jpg",
        "http://i.imgur.com/RSU6NxX.jpg",
        "http://i.imgur.com/hUWgS2S.jpg",
        "http://i.imgur.com/U8kaix0.jpg",
        "http://i.imgur.com/w3cEYpY.jpg",
        "http://i.imgur.com/ooSCD9T.jpg"}
    fmt.Println("downloading", len(fileList), "files")
    for _, url := range fileList {
        wg.Add(1)
        go func(url string) {
            err := downloadFile(url)
            if err != nil {
                fmt.Println("[error]", url, err)
            }
            wg.Done()
        }(url)
    }
    wg.Wait()
}
我不喜欢将WaitGroups传递给其他人,我更喜欢保持函数的简单性、阻塞性和顺序性,然后在更高的级别将并发性缝合在一起。这使您可以选择按顺序执行所有操作,而无需更改下载文件


我还添加了错误处理和固定名称,因此它们是camelCase。

添加到Calab的响应中,您的方法绝对没有问题,您所要做的就是传递一个指向sync.WaitGroup的指针


您正在复制您的WaitGroup;你应该给下载文件传递一个指向它的指针。Go有一些语法上的糖分,可以用指针接收器在非指针值上调用方法,但你应该真正考虑你传递的是什么,方法调用总是产生一个副本。此外,你忽略了错误。不要这样做。有使用camelCase的惯例吗?
func download_file(file_path string, wg *sync.WaitGroup) {
    defer wg.Done()
    ......
}
.....
        go download_file(url, &wg)
.....