为什么网络i/o的goroutine被阻止?

tux*_*fly 3 go goroutine

我在Ubuntu 13.04上使用了1.1开发

go version devel +ebe8bca920ad Wed May 15 15:34:47 2013 +1000 linux/386
Run Code Online (Sandbox Code Playgroud)

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

当协程阻塞时,例如通过调用阻塞系统调用,运行时自动将同一操作系统线程上的其他协同程序移动到另一个可运行的线程,这样它们就不会被阻塞.

我正在尝试编写一个可以使用goroutines下载大块文件的下载器,这是我提出的最好的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)
    }
}
Run Code Online (Sandbox Code Playgroud)

完整的脚本可以在https://github.com/tuxcanfly/godown/blob/master/godown.go上找到.

即使文件正在下载并正确保存,我也可以看到第二个块只在第一个块完成时才启动.

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

Ste*_*erg 17

你只有一个goroutine下载块.

第64行:

go download(*download_url, chunks, offset, file)
Run Code Online (Sandbox Code Playgroud)

你可能想要的是:

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

这将立即下载*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))
Run Code Online (Sandbox Code Playgroud)

对此

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

有关Range标头的更多信息,我建议阅读RFC2616中的第14.35节

  • 超越.很好的答案. (2认同)