Должен ли я использовать ThreadPools или параллельную библиотеку задач для операций, связанных с IO


В одном из моих проектов, который является своего рода агрегатором, я анализирую каналы, подкасты и так далее из интернета.

Если я использую последовательный подход, учитывая большое количество ресурсов, требуется довольно много времени, чтобы обработать их все (из-за проблем с сетью и тому подобного);

foreach(feed in feeds)
{
   read_from_web(feed)
   parse(feed)
}

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

ThreadPools наверняка будут обработайте работу для меня с рабочими потоками, и я получу то, что ожидаю (и в многоядерных процессорных средах другие ядра также будут использоваться).

совпадение

Но я все еще хочу рассмотреть TPL, поскольку это рекомендательный метод, но я немного обеспокоен этим. Прежде всего, я знаю, что TPL использует ThreadPools, но добавляет дополнительный уровень принятия решений. Меня больше всего беспокоит состояние, при котором присутствует одноядерная среда. Если я не ошибаюсь TPL начинается с числа рабочие потоки равны количеству доступных процессорных ядер в самом начале. Я действительно боюсь, что TPL даст аналогичные результаты последовательному подходу для моего случая, связанного с IO.

Итак, для операций, связанных с IO (в моем случае чтение ресурсов из интернета), лучше всего использовать ThreadPools и контролировать вещи, или лучше просто полагаться на TPL? Может ли TPL также использоваться в сценариях, связанных с IO?

Обновление: моя главная забота-это ... на одноядерный процессор среда ОСАГО, ведут себя как последовательный подход или он все еще будет предлагать параллелизм? Я уже читаю параллельное программирование с Microsoft .NET и поэтому книгу , но не смог найти точного ответа на этот вопрос.

Примечание: это перефразирование моего предыдущего вопроса [ возможно ли использовать параллелизм потоков и параллелизм вместе?Что было совершенно неправильно сформулировано.

6 72

6 ответов:

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

Легенда Теста

  • Itr: Итерация
  • Seq: Последовательный Подход.
  • PrlEx: Параллельные Расширения-Parallel.ForEach
  • TPL: параллельная библиотека задач
  • TPool: ThreadPool

Результаты Испытаний

одноядерный процессор [Win7-32] -- работает под управлением VMWare --

Test Environment: 1 physical cpus, 1 cores, 1 logical cpus.
Will be parsing a total of 10 feeds.
________________________________________________________________________________

Itr.    Seq.    PrlEx   TPL     TPool
________________________________________________________________________________

#1      10.82s  04.05s  02.69s  02.60s
#2      07.48s  03.18s  03.17s  02.91s
#3      07.66s  03.21s  01.90s  01.68s
#4      07.43s  01.65s  01.70s  01.76s
#5      07.81s  02.20s  01.75s  01.71s
#6      07.67s  03.25s  01.97s  01.63s
#7      08.14s  01.77s  01.72s  02.66s
#8      08.04s  03.01s  02.03s  01.75s
#9      08.80s  01.71s  01.67s  01.75s
#10     10.19s  02.23s  01.62s  01.74s
________________________________________________________________________________

Avg.    08.40s  02.63s  02.02s  02.02s
________________________________________________________________________________

одноядерный процессор [WinXP] -- работает под управлением VMWare --

Test Environment: 1 physical cpus, NotSupported cores, NotSupported logical cpus.
Will be parsing a total of 10 feeds.
________________________________________________________________________________

Itr.    Seq.    PrlEx   TPL     TPool
________________________________________________________________________________

#1      10.79s  04.05s  02.75s  02.13s
#2      07.53s  02.84s  02.08s  02.07s
#3      07.79s  03.74s  02.04s  02.07s
#4      08.28s  02.88s  02.73s  03.43s
#5      07.55s  02.59s  03.99s  03.19s
#6      07.50s  02.90s  02.83s  02.29s
#7      07.80s  04.32s  02.78s  02.67s
#8      07.65s  03.10s  02.07s  02.53s
#9      10.70s  02.61s  02.04s  02.10s
#10     08.98s  02.88s  02.09s  02.16s
________________________________________________________________________________

Avg.    08.46s  03.19s  02.54s  02.46s
________________________________________________________________________________

двухъядерный процессор [Win7-64]

Test Environment: 1 physical cpus, 2 cores, 2 logical cpus.
Will be parsing a total of 10 feeds.
________________________________________________________________________________

Itr.    Seq.    PrlEx   TPL     TPool
________________________________________________________________________________

#1      07.09s  02.28s  02.64s  01.79s
#2      06.04s  02.53s  01.96s  01.94s
#3      05.84s  02.18s  02.08s  02.34s
#4      06.00s  01.43s  01.69s  01.43s
#5      05.74s  01.61s  01.36s  01.49s
#6      05.92s  01.59s  01.73s  01.50s
#7      06.09s  01.44s  02.14s  02.37s
#8      06.37s  01.34s  01.46s  01.36s
#9      06.57s  01.30s  01.58s  01.67s
#10     06.06s  01.95s  02.88s  01.62s
________________________________________________________________________________

Avg.    06.17s  01.76s  01.95s  01.75s
________________________________________________________________________________

четырехъядерный процессор [Win7-64] -- поддерживается гиперпоточность --

Test Environment: 1 physical cpus, 4 cores, 8 logical cpus.
Will be parsing a total of 10 feeds.
________________________________________________________________________________

Itr.    Seq.    PrlEx   TPL     TPool
________________________________________________________________________________

#1      10.56s  02.03s  01.71s  01.69s
#2      07.42s  01.63s  01.71s  01.69s
#3      11.66s  01.69s  01.73s  01.61s
#4      07.52s  01.77s  01.63s  01.65s
#5      07.69s  02.32s  01.67s  01.62s
#6      07.31s  01.64s  01.53s  02.17s
#7      07.44s  02.56s  02.35s  02.31s
#8      08.36s  01.93s  01.73s  01.66s
#9      07.92s  02.15s  01.72s  01.65s
#10     07.60s  02.14s  01.68s  01.68s
________________________________________________________________________________

Avg.    08.35s  01.99s  01.75s  01.77s
________________________________________________________________________________

Обобщение

    Независимо от того, работаете ли вы в одноядерной среде или многоядерной, параллельные расширения, TPL и ThreadPool ведут себя одинаково и дают приблизительные результаты.
  • Тем не менее TPL имеет преимущества , такие как простая обработка исключений, поддержка отмены и способность легко возвращать результаты задачи . Хотя параллельные расширения также являются еще одной жизнеспособной альтернативой.

Запуск тестов самостоятельно

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

Обновление: исправлена исходная ссылка.

Если вы пытаетесь максимизировать пропускную способность для задач, связанных с IO, вы абсолютно должен объедините традиционные API модели асинхронной обработки (APM) с вашей работой на основе TPL. API APM-это единственный способ разблокировать поток ЦП, пока асинхронный обратный вызов ввода-вывода находится в ожидании. TPL предоставляет вспомогательный методTaskFactory::FromAsync для помощи в объединении кода APM и TPL.

Проверьте этот раздел .NET SDK на MSDN под названием TPL и традиционный .NET Асинхронное программирование для получения дополнительной информации о том, как объединить эти две модели программирования для достижения асинхронной нирваны.

Вы правы, что TPL действительно удаляет некоторые элементы управления, которые у вас есть, когда вы создаете свой собственный пул потоков. Но это правильно только в том случае, если вы не хотите копать глубже. TPL позволяет создавать длительные задачи, которые не являются частью пула потоков TPL и могут хорошо служить вашей цели. Опубликованная книга, которая является свободным чтениемпараллельное программирование с Microsoft .NET даст вам гораздо больше информации о том, как предполагается использовать TPL. У вас всегда есть возможность дать Паралле.Для, задачи явные параметры, сколько потоков должно быть выделено. Кроме того, вы можете заменить планировщик TPL на свой собственный, если хотите получить полный контроль.

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

Я действительно боюсь, что TPL даст аналогичные результаты последовательному подходу для моего случая, связанного с IO.

Я думаю, что так и будет. Что такое узкое место? Это разбор или загрузка? Многопоточность не очень поможет вам при загрузке из интернета.

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

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

ПС. "Ничего себе моя функция исполняется за 0.7 с вместо 0.9";)

Если вы распараллелите свои вызовы на URL-адреса, я думаю, что это улучшит ваше приложение, даже если у него есть только одно ядро. Взгляните на этот код:

var client = new HttpClient();
var urls = new[]{"a", "url", "to", "find"};

// due to the EAP pattern, this will run in parallel.
var tasks = urls.Select(c=> client.GetAsync(c));

var result = Tasks.WhenAll(task).ContinueWith(a=> AnalyzeThisWords(a.Result));
result.Wait(); // don't know if this is needed or it's correct to call wait

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

При использовании EAP количество задач не связано с количеством потоков.

Поскольку вы полагаетесь на Задачу GetAsync, http-клиент использует сетевой поток (сокет, tcp-клиент или что-то еще) и сигнализирует его чтобы вызвать событие, когда выполняется BeginRead/EndRead. Таким образом, никакие нити не вовлечены в этот момент.

После вызова завершения может быть создан новый поток, но это зависит от TaskScheduler (используется в вызове GetAsync/ContinueWith call), чтобы создать новый поток, использовать существующий поток или встроить задачу, чтобы использовать вызывающий поток.

Если AnalyzeThisWords блокируется слишком много времени, то вы начинаете получать узкие места, поскольку "обратный вызов" на ContinueWith выполняется из пула потоков работник.