Какова хорошая стратегия для параллельной обработки очереди? - PullRequest
3 голосов
/ 08 мая 2011

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

Я уже написал довольно тривиальный синхронный метод - сначала добавляем корневой каталог в очередь, затем удаляем каталог из очереди, ставим в очередь его подкаталоги и т. Д., Пока очередь не станет пустой. Я буду использовать ConcurrentQueue<T> для своей очереди, но уже понял, что мои циклы преждевременно остановятся. Первый поток удалит из корневого каталога очередь, и каждый другой поток сразу же увидит, что очередь пуста, и завершится, оставив первый поток единственным работающим. Я бы хотел, чтобы каждый поток зацикливался до тех пор, пока очередь не станет пустой, а затем дождался, пока другой поток поставил в очередь еще несколько каталогов, и продолжил. Мне нужна какая-то контрольная точка в моем цикле, чтобы ни один из потоков не завершился до тех пор, пока каждый поток не достигнет конца цикла, но я не уверен, что лучший способ сделать это без блокировки, когда на самом деле больше нет каталогов для процесс.

Ответы [ 3 ]

5 голосов
/ 08 мая 2011

Используйте параллельную библиотеку задач .

Создайте Task для обработки первой папки.При этом создайте Task для обработки каждой подпапки (рекурсивно) и задачу для каждого соответствующего файла.Затем дождитесь выполнения всех задач для этой папки.

Среда выполнения TPL будет использовать пул потоков, избегая создания потоков, что является дорогостоящей операцией.для небольших фрагментов работы.

Примечание:

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

1 Насколько я понимаю, в TPL при ожидании задач - используя метод TPL - TPL будет повторно использовать этот поток для других задач, пока не будет выполнено ожидание.

2 голосов
/ 08 мая 2011

Если вы хотите придерживаться концепции явной очереди, взгляните на класс BlockingCollection . Метод GetConsumingEnumerable () возвращает IEnumerable, который блокируется, когда в коллекции заканчивается количество элементов и продолжается, как только новые элементы становятся доступными. Это означает, что всякий раз, когда коллекция пуста, поток блокируется и, таким образом, предотвращает его преждевременную остановку.

Однако: в основном это очень полезно для сценариев производитель-потребитель. Я не уверен, относится ли ваша проблема к этой категории.

1 голос
/ 08 мая 2011

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

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

...