【问题标题】:Why are goroutines with network i/o being blocked?为什么带有网络 i/o 的 goroutine 会被阻塞?
【发布时间】:2013-05-20 04:50:37
【问题描述】:

我在 Ubuntu 13.04 上使用 go 1.1 开发

go version devel +ebe8bca920ad Wed May 15 15:34:47 2013 +1000 linux/386

根据http://golang.org/doc/faq#goroutines

当协程阻塞时,例如通过调用阻塞系统调用, 运行时自动将其他协程移动到同一 操作系统线程到不同的可运行线程,因此它们不会 被屏蔽。

我正在尝试编写一个下载器,它可以使用 goroutine 分块下载一个大文件 这是我想出的最好的 goroutine:

func download(uri string, chunks chan int, offset int, file *os.file) {
    for current := range chunks {

        fmt.println("downloading range: ", current, "-", current+offset)

        client := &http.client{}
        req, _ := http.newrequest("get", uri, nil)
        req.header.set("range: ", fmt.sprintf("bytes=%d-%d", current, current+offset))
        resp, err := client.do(req)
        if err != nil {
            panic(err)
        }
        defer resp.body.close()
        body, err := ioutil.readall(resp.body)
        if err != nil {
            panic(err)
        }
        file.write(body)
    }
}

完整的脚本可在https://github.com/tuxcanfly/godown/blob/master/godown.go获得

尽管文件已正确下载和保存,但我可以看到第二个块开始了 只有当第一个完成时。

分块下载不应该并行运行,还是我做错了什么?

【问题讨论】:

    标签: go goroutine


    【解决方案1】:

    你只有一个 goroutine 下载块。

    第 64 行:

    go download(*download_url, chunks, offset, file)
    

    你可能想要的是:

    for i := 0; i < *threads; i++ {
        go download(*download_url, chunks, offset, file)
    }
    

    这将立即下载*threads 块。


    在您让并发工作之后,您可能会注意到第 29 行并没有按照您的预期工作。如果块 1 在块 2 之前完成,则这些部分将被乱序写入。您可能想改用http://golang.org/pkg/os/#File.WriteAt


    您的 Range 标头也有两个问题。

    1. 您不下载其余部分。如果文件大小为 3002 并且您有 3 个线程,它将请求 0-1000、1000-2000、2000-3000,并且永远不会下载最后 2 个字节。
    2. 字节范围包括在内。这意味着您(正如您在前面的示例中看到的)两次下载一些字节。字节 1000 和 2000 被请求两次。当然,只要你写到正确的位置,你应该不会有太大的问题。

    第二个很容易通过更改第 19 行来解决

    req.Header.Set("Range: ", fmt.Sprintf("bytes=%d-%d", current, current+offset))
    

    到这里

    req.Header.Set("Range: ", fmt.Sprintf("bytes=%d-%d", current, current+offset-1))
    

    有关 Range 标头的更多信息,我建议阅读Section 14.35 in RFC2616

    【讨论】:

    • 超越。很好的答案。
    • 非常感谢!我对 goroutine 的调用方式有一些误解,我还在学习中。再次感谢!
    猜你喜欢
    • 1970-01-01
    • 2017-07-14
    • 1970-01-01
    • 1970-01-01
    • 2021-12-09
    • 2017-09-12
    • 2012-02-21
    • 2022-07-25
    • 1970-01-01
    相关资源
    最近更新 更多