2015-05-09 78 views
-1

我想下載文件並行地走,但我的代碼永遠不會退出:Golang併發下載僵局

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() 
} 

是什麼原因?我在這裏看過:Golang download multiple files in parallel using goroutines但我找不到解決方案。 調試此類代碼的最佳方式是什麼?

+7

你讓你的'WaitGroup'複印件;你應該傳遞一個'download_file'指針給它。 –

+0

Go有一些語法糖可以用指針接收器調用非指針值的方法,但是您應該真的考慮一下你傳遞的方法,並且方法調用總是會生成一個副本。 – Volker

+6

此外,你忽略了錯誤。不要這樣做。 – peterSO

回答

1

由於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 

我建議使用一個編輯器,當你保存文件,可以爲你做這個。例如針對Atom的go-plus

至於我想你應該重組像這樣的代碼:

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() 
} 

我不喜歡身邊路過WaitGroup S和喜歡保持功能簡單,阻斷和順序,然後縫合在一起併發在更高的水平。這使您可以按順序執行所有操作,而無需更改downloadFile

我還添加了錯誤處理和固定名稱,以便它們是camelCase。

+0

有使用camelCase的習慣嗎? – grotos

+0

@grotos是:http://golang.org/doc/effective_go.html#mixed-caps – Caleb

1

除了卡拉布的迴應,你的方法絕對沒有錯,你所要做的就是傳遞一個指向sync.WaitGroup的指針。

func download_file(file_path string, wg *sync.WaitGroup) { 
    defer wg.Done() 
    ...... 
} 
..... 
     go download_file(url, &wg) 
..... 

playground