2016-12-10 86 views
1

我應該在目錄和子目錄中處理大約8000個文件。如何使用線程/任務輕鬆處理每個文件並全部等待?如何處理大量文件

就目前而言,我用這個代碼

var files = Directory.EnumerateFileSystemEntries(@"E:\Nouveau dossier (2)", 
               "*.*", SearchOption.AllDirectories); 
foreach (var f in files) 
{ 
    ToDo(f); 
} 

但是它是非常非常慢。

回答

3

繼續記住,任何正確的答案要處理兩件事情。

  1. 讀取文件 - 這可能不是在並行很好地工作,特別是在適當的磁盤(而不是固態硬盤),因爲他們的頭部在每個文件的位置,並且不能並行進行,效果顯着。
  2. 處理文件 - 一旦數據在內存中,您可以在不同的內核上處理它們。

現在,這兩部分哪一部分需要更多時間?如果它正在讀取文件,並且通常情況會是這樣,那麼使用多個內核就沒有任何幫助。他們仍然需要等待數據進入。

我給你的建議是做一個實驗。不要處理文件,但只能讀取它們。讓你的ToDo()函數只讀取文件到最後。這是您完成整個工作的最短時間。

然後嘗試並行讀取文件,但要準備看到,它需要更多的時間比以前...你可以是這樣做的:

Parallel.ForEach(files, ReadToEnd); 

這是假設ReadToEnd()是您的測試功能,只是讀取文件的內容。

+0

如果該文件的處理是耗時的部分,運算不得創建比核心數更多的線程。當一個線程以一個(或n個)文件結束時,它應該獲取下一個文件數據(或下一個n個文件數據)進行處理,在此操作期間鎖定文件數據列表。 – Graffito

+0

這是真的,但我不希望看到處理花費的時間比加載內容更多的情況。這也是爲什麼我建議在沒有處理的情況下測量性能,只有在加載的情況下才能測量性能,並且看到 - 如果這與包括處理的時間大致相同,那麼將IO看起來成爲瓶頸是沒有意義的。 –

+0

解析XML文件通常比閱讀它們要長。 – Graffito

1

可以使用Parallel類。

請看下面的例子:

class Program 
{ 

    static void Main(string[] args) 
    { 
    var files = Directory.EnumerateFileSystemEntries(@"C:\Users\Myleo\Pictures", "*.*", SearchOption.AllDirectories); 
    var program = new Program(); 
    var result = program.ProcessInParallelWithCounter(files); 
    Console.WriteLine("count: {0}", result); 

    #if DEBUG 
     Console.ReadKey(); 
    #endif 
} 

private void ProcessInParallel(IEnumerable<string> files) 
{ 
    // process 
    Parallel.ForEach(files, Process); 
} 

private int ProcessInParallelWithCounter(IEnumerable<string> files) 
{ 
    // process and count 
    var counter = 0; 
    Parallel.ForEach(
     files, 
     () => 0, 
     (file, loopState, localCount) => 
             { 
              Process(file); 
              return ++localCount; 
             }, 
     count => Interlocked.Add(ref counter, count)); 
    return counter; 
} 

private void Process(string file) 
{ 
    // your code. 
} 

}

0

因爲讀取文件的IO操作,那麼async/await做法似乎​​這個任務最好的方法。

您不需要浪費線程來等待IO讀取或寫入文件。讀取或寫入文件是包含「等待」IO設備響應的操作。創建單獨的線程什麼都不做 - >只有等待就是浪費資源並且不會給你的應用程序帶來任何價值。

通過使用async/await您可以只用一個線程完成相同的工作。當第一個任務正在等待文件被讀取時,另一個任務將會啓動,依此類推。

你可以讓你ToDo方法作品異步

public async Task ToDoAsync(string file) 
{ 
    using (var fileReader = File.OpenText(file)) 
    { 
     var allFile = await fileReader.ReadToEndAsync(); 
     // and do something 
    } 
} 

,然後用它

var files = Directory.EnumerateFileSystemEntries(@"E:\Nouveau dossier (2)", "*.*", SearchOption.AllDirectories); 

var tasks = new List<Task>(); 
foreach (var f in files) 
{ 
    var task = ToDoAsync(f); 
    tasks.Add(task); 
} 

await Task.WhenAll(tasks.ToArray()); 

因此獲得更好的性能和使用您需要將您的邏輯分爲兩個部分資源的更好,因爲在@Zoran答案中提到。

  • 可以在異步的方式進行,可以在「水貨」來完成
  • 處理數據讀取文件
0

您可以運行在單獨的任務

ToDo
var files = Directory.EnumerateFileSystemEntries(@"E:\Nouveau dossier (2)", "*.*", SearchOption.AllDirectories); 
List<Task> tasks = new List<Task>(); 
foreach (var f in files) 
{ 
    var local = f; 
    var tast = Task.Run(() => ToDo(local)); 

    tasks.Add(task); 
} 

Task.WhenAll(tasks.ToArray()); 
+0

謝謝,我會嘗試,但我可以執行8000個任務嗎?這不危險? –

+0

這不會爲每個操作創建一個線程,它會將它們放在默認的TaskScheduler(https://msdn.microsoft.com/en-us/library/system.threading.tasks.taskscheduler(v = vs.110 )的.aspx) –