2010-09-12 4 views
2

В прошлом я использовал ThreadPool.QueueUserWorkItem для создания нескольких потоков из класса менеджера. Этот класс менеджера подписывается на событие в этих порожденных потоках, которое возникает при завершении работы потока. Затем класс менеджера может обрабатывать запись вывода в текстовый файл с помощью lock для предотвращения любых условий гонки..NET Parallel.ForEach, StreamWriter output + thread safe

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

Основной план моей реализации:

public class Directory 
{ 
    public string Path; 

    public Directory(string path) 
    { 
     Path = path; 
    } 

    public void Scan() 
    { 
     Parallel.ForEach(new DirectoryInfo(Path).GetDirectories(), 
         delegate(DirectoryInfo di) 
         { 
          var d = new Directory(di.FullName); 
          d.Scan(); 
          //Output to text file. 
         }); 

    } 
} 

Что я получаю идя:

new Directory(@"c:\blah").Scan(); 

Любые идеи, чтобы указать меня в правильном направлении, было бы здорово. У меня есть несколько человек, но я ищу лучшую практику. Я прочитал this post, но он не содержит решения, которое мне помогает.

+0

Что вы пишете в файл? Вы можете выразить это в запросе? –

+0

Вывод - это объект, сериализованный как строка JSON с использованием библиотеки Newtonsoft.Json. – youwhut

+0

Что находится в '// Вывод в текстовый файл'? Если это вызывает событие, которое вызывает прямо в некотором 'lock()', где происходит реальная обработка, это не приведет к тому, что это будет сделано параллельно. Итак, как выглядит текущее событие с точки зрения того, что прошло между рабочими и менеджером? –

ответ

1

Использовать EnumerateDirectories (Fx 4) вместо GetDirectories. Ваш текущий код не будет работать параллельно.

В остальном это зависит от того, нужен ли вам результат в порядке или нет.
Если вам не нужен заказ, вы можете просто заблокировать выходной поток (со вспомогательным объектом), написать и продолжить. Не нужно усложнять Событие.
Если вы хотите сохранить заказ,

Нажмите вывод в очередь. Обработать очередь, когда ForEach завершен или запустить отдельную задачу (Consumer), чтобы написать ее как можно скорее. Это будет типичный образец производителя/потребителя.

Обратите внимание: при выполнении обработки Parallel становится очень сложно поддерживать порядок написания Каталогов.

+0

Зачем ему нужна очередь? Разве он не может просто сделать «AsOrdered()», чтобы вернуть все в исходном порядке? – Gabe

+0

@GAbe: Я не вижу, как. Обработка Dirs будет меняться во времени. EnumDirs уже упорядочен (а не параллелен). –

+0

Хенк: Я представляю что-то подобное: 'outfile.Write (EnumDirs(). AsParallel(). Выберите (f => Scan (f)). AsOrdered())', чтобы обработка 'Scan' была параллельна но выход находится в порядке перечисления. – Gabe

0

Для начала я бы выделил концепцию перечисления файлов из концепции их обработки.

Возможно сделать ваш Directory класс реализации IEnumerable<FileInfo> и лениво перечислить все файлы, используя рекурсию, EnumerateDirectories и EnumerateFiles. (См. http://msdn.microsoft.com/en-us/library/dd997370.aspx).

Теперь вы можете решить проблему потребления этого IEnumerable и обработать его, не смешивая код, чтобы перезаписать каталоги.

Создать выходной поток. Перечислите IEnumerable<FileInfo> и зажгите Task для каждого: см. http://msdn.microsoft.com/en-us/library/dd321424.aspx. Внутри этой задачи после прочтения файла и создания выходной строки lock() и записи в выходной поток.

В качестве альтернативы, и, возможно, немного чище, начать отдельный потребитель Task, который делает запись и использовать BlockingCollection для передачи данных между производителями и потребителями (см http://msdn.microsoft.com/en-us/library/dd267312.aspx).

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

См. Также http://reedcopsey.com/2010/03/17/parallelism-in-net-part-14-the-different-forms-of-task/ и все записи в Риге в TPL.

См. Также усилия по связыванию TPL и RX, например. http://blogs.msdn.com/b/pfxteam/archive/2010/04/04/9990349.aspx, который обеспечит более чистый синтаксис для создания и потребления в такой ситуации.