2017-05-30 64 views
3

我想獲取目錄中文件的文件信息(文件名爲&大小(以字節爲單位))。但是有很多子目錄(〜1000)和文件(〜40 000)。併發文件系統掃描

其實我的解決方案是使用filepath.Walk()來獲取每個文件的文件信息。但是這很長。

func visit(path string, f os.FileInfo, err error) error { 
    if f.Mode().IsRegular() { 
     fmt.Printf("Visited: %s File name: %s Size: %d bytes\n", path, f.Name(), f.Size()) 
    } 
    return nil 
} 
func main() { 
    flag.Parse() 
    root := "C:/Users/HERNOUX-06523/go/src/boilerpipe" //flag.Arg(0) 
    filepath.Walk(root, visit) 
} 

是否可以使用filepath.Walk()進行並行/並行處理?

+0

創建爲每個文件的goroutine不會幫助你在所有的,我建議你創建一個工作夠程僅處理目錄,鏈接:https://開頭blog.golang.org/pipelines,https://gobyexample.com/worker-pools –

回答

6

您可以通過修改visit()函數不進入子文件夾來執行併發處理,但爲每個子文件夾啓動一個新的goroutine。

爲此,如果條目是目錄,請從visit()函數返回特殊的filepath.SkipDir錯誤。不要忘記檢查visit()裏面的path是否應該處理子例程,因爲這也會傳遞給visit(),如果沒有這個檢查,你會無休止地爲初始文件夾啓動goroutines。

此外,您還需要某種「計數器」,以瞭解有多少個goroutine仍在後臺工作,因此您可以使用sync.WaitGroup

下面是一個簡單的實現這一點:

var wg sync.WaitGroup 

func walkDir(dir string) { 
    defer wg.Done() 

    visit := func(path string, f os.FileInfo, err error) error { 
     if f.IsDir() && path != dir { 
      wg.Add(1) 
      go walkDir(path) 
      return filepath.SkipDir 
     } 
     if f.Mode().IsRegular() { 
      fmt.Printf("Visited: %s File name: %s Size: %d bytes\n", 
       path, f.Name(), f.Size()) 
     } 
     return nil 
    } 

    filepath.Walk(dir, visit) 
} 

func main() { 
    flag.Parse() 
    root := "folder/to/walk" //flag.Arg(0) 

    wg.Add(1) 
    walkDir(root) 
    wg.Wait() 
} 

一些注意事項:

根據子文件夾中的文件的「分配」,這可能不是完全利用CPU /存儲,彷彿例如所有文件的99%都在一個子文件夾中,那麼goroutine仍然佔用大部分時間。

另外請注意,fmt.Printf()調用是序列化的,這樣也會減慢進程。我認爲這只是一個例子,實際上你會在內存中做一些處理/統計。不要忘記同時保護從visit()函數訪問的變量。

不要擔心大量的子文件夾。這是正常的,Go運行時能夠處理數十萬個goroutine。

另請注意,最有可能的性能瓶頸將是你的存儲/硬盤速度,所以你可能無法獲得你想要的性能。在某個點(您的硬盤限制)之後,您將無法提高性能。

也爲每個子文件夾啓動一個新的goroutine可能不是最佳的,它可能是通過限制遍歷文件夾的goroutine的數量來獲得更好的性能。爲此,檢查和使用工作池:

Is this an idiomatic worker thread pool in Go?

+0

我不掌握好生產者 - 消費者模式。 'producer()'將子文件夾發送到步行&'consumer()'以mycase打印。 goroutines行走文件夾的數量受'consumer'數量的限制。 – LeMoussel

+0

@LeMoussel是的,類似的東西。在我的例子中,不是用'go'啓動一個新的goroutine,你只需在消費者「監視」的通道上發送'path'來取出路徑以「walk」(通過執行'walkDir()')。 – icza