2016-12-10 2 views
1

Я должен обработать около 8000 файлов в каталоге и подкаталоге. Как я могу обрабатывать каждый файл с помощью потока/задачи и ждать все?Как обрабатывать большое количество файлов

На данный момент, я использую этот код

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

Однако это очень и очень медленно.

ответ

3

Держите на виду, что любой правильный ответ должен иметь дело с двумя вещами.

  1. Чтение файлов - это не может хорошо работать параллельно, особенно на собственных дисках (не SSD), так как они должны расположить голову над каждым из файлов и что не может быть сделано параллельно, очевидно.
  2. Обработка файлов - после того, как данные находятся в памяти, вы можете обрабатывать их на отдельных ядрах.

Теперь, какая часть этих двух занимает больше времени? Если это чтение файлов, и чаще всего это будет так, то нет никакой помощи в использовании нескольких ядер. Им все равно придется ждать, пока данные войдут.

Мой совет вам - сделать эксперимент. Не обрабатывайте файлы, а читайте их только. Сделайте свою функцию ToDo(), просто прочитайте файл до конца. Это минимальное время, которое вы можете получить за всю свою работу.

Затем попытайтесь читать файлы параллельно, но будьте готовы, чтобы увидеть, что это занимает больше времени, чем раньше ... Вы можете сделать это так:

Parallel.ForEach(files, ReadToEnd); 

Это предполагает, что ReadToEnd() ваша тестовая функция, которая просто читает содержимое файла.

+0

Если обработка файла является трудоемкой частью, оп не должен создавать больше потоков, чем число ядер. Когда поток заканчивается одним (или n) файлом, он должен получить следующие данные файла (или следующие данные n файлов) для обработки, блокируя список данных файлов во время этой операции. – Graffito

+0

Это правда, но я не ожидаю увидеть случай, когда обработка занимает больше времени, чем загрузка содержимого. Именно поэтому я предложил измерить производительность без обработки, только с загрузкой и увидеть - если это примерно то же самое, что и время, которое включает в себя обработку, тогда нет смысла распараллеливать что-либо, поскольку IO кажется узким местом. –

+0

Разбор XML-файлов обычно длиннее, чем чтение. – Graffito

3

Попробуйте метод Parallel.ForEach. Но это не будет гарантией какого-либо усиления выигрыша, поскольку IO - это загрузочное окно.

Parallel.ForEach(files, (f) => { 
    ToDo(f); 
}); 

system.threading.tasks.parallel.foreach

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

Поскольку чтение файлов являются операции ввода-вывода, то 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.

  • Чтение файлов, которые могут быть сделаны в асинхронном способом
  • обработки данных, которые можно сделать в «Parallel»
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) –

1

Просто сразу же, вот несколько способов использования нескольких потоков для более эффективного использования ресурсов процессора для ускорения обработки, при условии, что это где узкое место есть. Например, вы вряд ли сможете улучшить производительность, если узким местом является дисковый ввод-вывод, например.

  1. ThreadPool.QueueUserWorkItem Method
  2. Task Parallel Library (TPL) ForEach
  3. TaskFactory.StartNew Method
Смежные вопросы