为了账号安全,请及时绑定邮箱和手机立即绑定

使用 goroutines 下载文件?

使用 goroutines 下载文件?

Go
慕码人2483693 2021-11-08 19:17:04
我是 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.WaitGroupwg.Add(1)go imageDownloader(url, file, &wg)wg.Wait()}我是否错误地使用了 WaitGroups?什么可能导致这种情况,我该如何解决?更新:解决了。我已将该wg.add()函数置于循环之外。:(
查看完整描述

3 回答

?
繁星coding

TA贡献1797条经验 获得超4个赞

虽然我不确定究竟是什么导致了您的问题,但这里有两个选项可以让您恢复正常工作。

首先,查看如何使用同步库中的等待组示例,尝试defer wg.Done()在函数的开头调用以确保即使 goroutine 意外结束,等待组也正确递减。

其次,io.Copy返回一个你没有检查的错误。无论如何,这都不是很好的做法,但在您的特定情况下,它会阻止您查看复制例程中是否确实存在错误。检查并妥善处理。它还返回写入的字节数,这也可能对您有所帮助。



查看完整回答
反对 回复 2021-11-08
?
PIPIONE

TA贡献1829条经验 获得超9个赞

您的示例在使用 WaitGroups 方面没有任何明显错误。只要您调用wg.Add()的数量与您启动的 goroutine 的数量相同,或者每次启动新的 goroutine 时都将其增加 1,这应该是正确的。

但是,您在 goroutine 中调用os.Exitpanic针对某些错误条件,因此如果您有多个这些正在运行,无论是否使用 WaitGroups,其中任何一个的失败都会终止所有这些。如果它在没有恐慌消息的情况下失败,我会看看这os.Exit(1)条线。

defer wg.Done()在函数开始时使用 go 也是一种很好的做法,这样即使发生错误,goroutine 仍然会减少其计数器。这样,如果 goroutine 之一返回错误,您的主线程将不会在完成时挂起。


查看完整回答
反对 回复 2021-11-08
?
一只名叫tom的猫

TA贡献1906条经验 获得超3个赞

我会在您的示例中进行的一项更改是defer当您使用Done. 我认为这defer ws.Done()应该是您函数中的第一条语句。


我喜欢WaitGroup的简单。但是,我不喜欢我们需要传递对 goroutine 的引用,因为这意味着并发逻辑将与您的业务逻辑混合。


所以我想出了这个通用函数来为我解决这个问题:


// 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)

    }

}

所以你的例子可以这样解决:


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


查看完整回答
反对 回复 2021-11-08
  • 3 回答
  • 0 关注
  • 213 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信