2015-06-22 3 views
0

В настоящее время у меня есть .txt-файл из примерно 170 000 jpg-файлов, и я читаю их все в List (fileNames).Поиск по нескольким темам в одной папке в то же время

Я хочу найти одну папку (в этой папке есть подпапки), чтобы проверить, существует ли каждый файл в именах файлов в этой папке, и если да, скопируйте его в новую папку.

Я делал приблизительную оценку, но каждый поиск и копирование для каждого имени файла в файлах имен занимает около 0,5 секунд. Таким образом, 170 000 секунд составляют примерно 48 часов, поэтому разделите на 2, что займет около 24 часов, чтобы мое приложение искало каждое имя файла, используя 1 поток! Очевидно, это слишком долго, поэтому я хочу сузить это и ускорить процесс. Каков наилучший способ сделать это с помощью многопоточности?

В настоящее время я думал создать 20 отдельных потоков и разбить мой список (имена файлов) на 20 разных списков и искать файлы одновременно. Например, я бы 20 различных потоков выполнения ниже в то же время:

  foreach (string str in fileNames) 
      { 
       foreach (var file in Directory.GetFiles(folderToCheckForFileName, str, SearchOption.AllDirectories)) 
       { 
        string combinedPath = Path.Combine(newTargetDirectory, Path.GetFileName(file)); 
        if (!File.Exists(combinedPath)) 
        { 
         File.Copy(file, combinedPath); 
        } 
       } 
      } 

ОБНОВЛЕНО ПОКАЗАТЬ МОЕ РЕШЕНИЕ НИЖЕ:

  string[] folderToCheckForFileNames = Directory.GetFiles("C:\\Users\\Alex\\Desktop\\ok", "*.jpg", SearchOption.AllDirectories); 

      foreach(string str in fileNames) 
      { 
       Parallel.ForEach(folderToCheckForFileNames, currentFile => 
        { 
         string filename = Path.GetFileName(currentFile); 
         if (str == filename) 
         { 
          string combinedPath = Path.Combine(targetDir, filename); 
          if (!File.Exists(combinedPath)) 
          { 
           File.Copy(currentFile, combinedPath); 
           Console.WriteLine("FOUND A MATCH AND COPIED" + currentFile); 
          } 
         } 

        } 
       ); 

      } 

Спасибо всем за ваш вклад! Очень ценим!

+1

Если я читаю вас правильно, то почему бы не прочитать все имена файлов, как только в памяти, как HashSet, а затем использовать его для поиска файла. Что касается ускорения дискового ввода-вывода с несколькими потоками, то это только до сих пор. После того как диск IO превышен, неважно, сколько потоков у вас есть. – cost

+0

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

+0

Вы пытались использовать TPL foreach? https://msdn.microsoft.com/en-us/library/dd460720(v=vs.110).aspx – qamar

ответ

0

Вместо того, чтобы использовать обычное высказывание Еогеасп в выполнении поиска, вы должны использовать Parallel LINQ. Parallel linq сочетает в себе простоту и удобочитаемость синтаксиса LINQ с возможностью параллельного программирования. Также как код, предназначенный для параллельной библиотеки задач. Это защитит вас от малой обработки нисходящего потока и вероятных исключений (трудно найти/отлаживать исключения), разделив вашу работу среди многих потоков. Таким образом, вы могли бы сделать что-то вроде этого:

fileNames.AsParallel().ForAll(str => 
      { 
       var files = Directory.GetFiles(folderToCheckForFileName, str, SearchOption.AllDirectories); 
       files.AsParallel().ForAll(file => 
       { 
        if (!string.IsNullOrEmpty(file)) 
        { 
         string combinedPath = Path.Combine(newTargetDirectory, Path.GetFileName(file)); 
         if (!File.Exists(combinedPath)) 
         { 
          File.Copy(file, combinedPath); 
         } 
        } 
       }); 
      }); 
+0

Я пошел с параллельной петлей foreach благодаря qamar выше. Это похоже на ту же концепцию, за исключением немного легче читать для меня, чем мой код. Я опубликую обновление моего кода выше. В чем разница между решением, которое я разработал с помощью qamar и вашего? –

+0

Между ними нет большой разницы. Оба они представляют собой петлевые конструкции, хотя 'Parallel.Foreach()' более популярен. Но 'Parallel.ForAll() обычно используется в конце возможного сложного запроса PLINQ. Поэтому 'Parallel.Foreach()' лучший выбор для вас в вашем случае – Cizaphil

0

20 разных потоков не помогут, если ваш компьютер имеет менее 20 ядер. Фактически, это может сделать процесс медленнее, потому что вы должны: 1) вынуждены тратить время переключение контекста между каждым потоком (который является способом вашего процессора для эмуляции более 1 потока/ядра) и 2) Thread в .NET резервирует 1 МБ для его стек, который довольно здоров.

Вместо этого попробуйте разделить свои входы/выходы на рабочие нагрузки async, используя Task.Run для частей, связанных с процессором. Кроме того, держите свой номер Tasks, возможно, от 4 до 8 при макс.

Пример кода:

var tasks = new Task[8]; 
var names = fileNames.ToArray(); 
for (int i = 0; i < tasks.Length; i++) 
{ 
    int index = i; 
    tasks[i] = Task.Run(() => 
    { 
     for (int current = index; current < names.Length; current += 8) 
     { 
      // execute the workload 
      string str = names[current]; 
      foreach (var file in Directory.GetFiles(folderToCheckForFileName, str, SearchOption.AllDirectories)) 
      { 
       string combinedPath = Path.Combine(newTargetDirectory, Path.GetFileName(file)); 
       if (!File.Exists(combinedPath)) 
       { 
        File.Copy(file, combinedPath); 
       } 
      } 
     } 
    }); 
} 
Task.WaitAll(tasks); 
+0

Вместо этого я пошел с параллельным циклом foreach. Я буду помнить об этом, чтобы попробовать этот метод в будущем, спасибо. Любые плюсы или минусы между этими двумя? –

+0

Хм, кажется, вы действительно выбрали [лучшее решение] (http://stackoverflow.com/questions/5009181/parallel-foreach-vs-task-factory-startnew). Разница в том, что 'Parallel.ForEach' является синхронным и блокируется до тех пор, пока все не будет завершено, но мы уже сделали это с тех пор, как в конце мы выполнили' Task.WaitAll'. Кроме того, 'Parallel.ForEach' использует' Partitioner 'для равномерного распределения задач. См. Ссылку, которую я опубликовал для получения более подробной информации. –