猿问

使用 goroutines 下载文件?

我是 Go 的新手,我正在学习如何使用 goroutines。


我有一个下载图片的功能:


func imageDownloader(uri string, filename string) {

    fmt.Println("starting download for ", uri)


    outFile, err := os.Create(filename)

    defer outFile.Close()

    if err != nil {

        os.Exit(1)

    }


    client := &http.Client{}


    req, err := http.NewRequest("GET", uri, nil)


    resp, err := client.Do(req)

    defer resp.Body.Close()


    if err != nil {

        panic(err)

    }


    header := resp.ContentLength

    bar := pb.New(int(header))

    rd := bar.NewProxyReader(resp.Body)

    // and copy from reader

    io.Copy(outFile, rd)

}

当我作为另一个函数的一部分单独调用时,它会完全下载图像并且没有截断的数据。


但是,当我尝试修改它以使其成为 goroutine 时,图像通常会被截断或零长度文件。


func imageDownloader(uri string, filename string, wg *sync.WaitGroup) {

    ...

    io.Copy(outFile, rd)

    wg.Done()

}


func main() {

var wg sync.WaitGroup

wg.Add(1)

go imageDownloader(url, file, &wg)

wg.Wait()

}

我是否错误地使用了 WaitGroups?什么可能导致这种情况,我该如何解决?


更新:


解决了。我已将该wg.add()函数置于循环之外。:(


慕码人2483693
浏览 214回答 3
3回答

繁星coding

虽然我不确定究竟是什么导致了您的问题,但这里有两个选项可以让您恢复正常工作。首先,查看如何使用同步库中的等待组的示例,尝试defer wg.Done()在函数的开头调用以确保即使 goroutine 意外结束,等待组也正确递减。其次,io.Copy返回一个你没有检查的错误。无论如何,这都不是很好的做法,但在您的特定情况下,它会阻止您查看复制例程中是否确实存在错误。检查并妥善处理。它还返回写入的字节数,这也可能对您有所帮助。

PIPIONE

您的示例在使用 WaitGroups 方面没有任何明显错误。只要您调用wg.Add()的数量与您启动的 goroutine 的数量相同,或者每次启动新的 goroutine 时都将其增加 1,这应该是正确的。但是,您在 goroutine 中调用os.Exit和panic针对某些错误条件,因此如果您有多个这些正在运行,无论是否使用 WaitGroups,其中任何一个的失败都会终止所有这些。如果它在没有恐慌消息的情况下失败,我会看看这os.Exit(1)条线。defer wg.Done()在函数开始时使用 go 也是一种很好的做法,这样即使发生错误,goroutine 仍然会减少其计数器。这样,如果 goroutine 之一返回错误,您的主线程将不会在完成时挂起。

一只名叫tom的猫

我会在您的示例中进行的一项更改是defer当您使用Done. 我认为这defer ws.Done()应该是您函数中的第一条语句。我喜欢WaitGroup的简单。但是,我不喜欢我们需要传递对 goroutine 的引用,因为这意味着并发逻辑将与您的业务逻辑混合。所以我想出了这个通用函数来为我解决这个问题:// Parallelize parallelizes the function callsfunc 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)    }}所以你的例子可以这样解决:func imageDownloader(uri string, filename string) {    ...    io.Copy(outFile, rd)}func main() {    functions := []func(){}    list := make([]Object, 5)    for _, object := range list {        function := func(obj Object){             imageDownloader(object.uri, object.filename)         }(object)        functions = append(functions, function)    }    Parallelize(functions...)            fmt.Println("Done")}如果你想使用它,你可以在这里找到它https://github.com/shomali11/util
随时随地看视频慕课网APP

相关分类

Go
我要回答