我正在嘗試使用公司的 api 將一堆檔案上傳到他們提供的存盤服務。(基本上到我的帳戶)。我有很多檔案,比如 40-50 之類的。我得到了檔案的完整路徑并使用了os.Open,這樣我就可以通過 io.Reader。我確實嘗試在client.Files.Upload()不使用的情況下使用,goroutines但上傳它們花了很多時間并決定使用goroutines. 這是我嘗試過的實作。當我運行該程式時,它只上傳一個檔案,該檔案是大小最小的檔案或等待很長時間的檔案。它有什么問題?是不是每次 for 回圈運行時都會創建一個goroutine繼續其回圈并為每個創建file?如何使其盡可能快goroutines?
var filePaths []string
var wg sync.WaitGroup
// fills the string of slice with fullpath of files.
func fill() {
filepath.Walk(rootpath, func(path string, info os.FileInfo, err error) error {
if !info.IsDir() {
filePaths = append(filePaths, path)
}
if err != nil {
fmt.Println("ERROR:", err)
}
return nil
})
}
func main() {
fill()
tokenSource := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: token})
oauthClient := oauth2.NewClient(context.TODO(), tokenSource)
client := putio.NewClient(oauthClient)
for _, path := range filePaths {
wg.Add(1)
go func() {
defer wg.Done()
f, err := os.Open(path)
if err != nil {
log.Println("err:OPEN", err)
}
upload, err := client.Files.Upload(context.TODO(), f, path, 0)
if err != nil {
log.Println("error uploading file:", err)
}
fmt.Println(upload)
}()
}
wg.Wait()
}
uj5u.com熱心網友回復:
考慮這樣的作業池模式:https : //go.dev/play/p/p6SErj3L6Yc
在這個示例應用程式中,我去掉了 API 呼叫,只列出了檔案名。這使它在操場上作業。
- 啟動固定數量的作業程式 goroutine。我們將使用一個頻道來分發他們的作業,我們將關閉該頻道以傳達作業的結束。這個數字可以是 1 或 1000 個例程,或者更多。應根據您的 putio API 可以合理預期支持的并發 API 運算元量來選擇該數量。
paths是chan string我們將用于此目的的。- 作業人員
range通過paths頻道接收要上傳的新檔案路徑
package main
import (
"fmt"
"os"
"path/filepath"
"sync"
)
func main() {
paths := make(chan string)
var wg = new(sync.WaitGroup)
for i := 0; i < 10; i {
wg.Add(1)
go worker(paths, wg)
}
if err := filepath.Walk("/usr", func(path string, info os.FileInfo, err error) error {
if err != nil {
return fmt.Errorf("Failed to walk directory: %T %w", err, err)
}
if info.IsDir() {
return nil
}
paths <- path
return nil
}); err != nil {
panic(fmt.Errorf("failed Walk: %w", err))
}
close(paths)
wg.Wait()
}
func worker(paths <-chan string, wg *sync.WaitGroup) {
defer wg.Done()
for path := range paths {
// do upload.
fmt.Println(path)
}
}
這種模式可以無限地處理大量檔案,而無需在處理之前將整個串列加載到記憶體中。如您所見,這并沒有使代碼更復雜——實際上,它更簡單。
當我運行程式時,它只上傳一個檔案
函式字面量繼承了它們被定義的范圍。這就是為什么我們的代碼只列出了一個路徑path——for 回圈中的變數作用域被每個 go 例程共享,所以當那個變數發生變化時,所有的例程都會接受變化。
除非您確實想繼承范圍,否則請避免使用函式文字。在全域作用域定義的函式不繼承任何作用域,您必須將所有相關變數傳遞給這些函式。這是一件好事 - 它使函式更易于理解并使變數“所有權”轉換更加明確。
使用函式文字的適當情況可能是os.Walk引數;它的引數由os.Walk定義范圍定義,因此定義范圍是訪問其他值的一種方式 - 例如paths在我們的例子中的通道。
說到作用域,應該避免使用全域變數,除非它們的使用范圍是真正的全域變數。更喜歡在函式之間傳遞變數而不是共享全域變數。同樣,這使變數所有權變得明確,并且易于理解哪些函式可以訪問哪些變數,哪些不能訪問。您的等待組和您的等待組都沒有filePaths任何理由成為全球性的。
f, err := os.Open(path)
不要忘記關閉您打開的任何檔案。當您處理 40 或 50 個檔案時,讓所有這些打開的檔案句柄堆積起來直到程式結束還不錯,但這是您程式中的定時炸彈,當檔案數量超過ulimit允許的數量時就會爆炸打開檔案。因為函式執行大大超出了檔案需要打開的部分,defer在這種情況下沒有意義。我會f.Close()在上傳檔案后使用顯式。
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/373116.html
下一篇:將字串傳遞給Go中的處理程式函式
