Должен ли я использовать ThreadPools или параллельную библиотеку задач для операций с привязкой к IO
В одном из моих проектов, которые похожи на агрегатор, я разбираю фиды, подкасты и т.д. из Интернета.
Если я использую последовательный подход, учитывая, что большое количество ресурсов, для их обработки требуется достаточно времени (из-за проблем с сетью и аналогичных материалов);
foreach(feed in feeds)
{
read_from_web(feed)
parse(feed)
}
Итак, я хочу реализовать concurrency и не мог решить, должен ли я в основном использовать ThreadPools для обработки с рабочими потоками или просто полагаться на TPL, чтобы отсортировать его.
ThreadPools наверняка обработает задание для меня с рабочими потоками, и я получу то, что ожидаю (и в многоядерных средах ЦП также будут использованы и другие ядра).
![concurrency]()
Но я все еще хочу рассматривать TPL тоже, поскольку он рекомендует метод, но я немного обеспокоен этим. Прежде всего, я знаю, что TPL использует ThreadPools, но добавляет дополнительный уровень принятия решений. Я в основном обеспокоен условием присутствия одноядерной среды. Если я не ошибаюсь, TPL начинается с числа рабочих потоков, равных числу доступных CPU-ядер в самом начале. Я боюсь, что TPL даст похожие результаты для последовательного подхода к моему делу с привязкой к IO.
Итак, для операций с привязкой IO (в моем случае чтения ресурсов из Интернета) лучше всего использовать ThreadPools и управлять вещами, или лучше просто полагаться на TPL? Может ли TPL также использоваться в сценариях, связанных с IO?
Обновление. Моя главная проблема заключается в том, что в одноядерном процессоре среда будет TPL просто вести себя как последовательный подход или будет ли она предлагать concurrency? Я уже читаю Параллельное программирование с помощью Microsoft.NET, и поэтому книги, но не смог найти точного ответа для этого.
Примечание: это повторная фраза моего предыдущего вопроса [Можно ли использовать thread- concurrency и parallelism вместе?], что было довольно неправильно сформулировано.
Ответы
Ответ 1
Итак, я решил написать тесты для этого и увидеть его на практических данных.
Test Legend
- Ит: Итерация
- 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] - Поддерживается HyprerThreading -
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 ведут себя одинаково и дают приблизительные результаты.
- преимущества , такие как легкая обработка исключений, поддержка отмены и возможность легко возвращать результаты задачи. Хотя параллельные расширения также являются альтернативной альтернативой.
Выполнение тестов самостоятельно
Здесь вы можете скачать источник и запустить его самостоятельно. Если вы сможете опубликовать результаты, я также добавлю их.
Обновление: Исправлена ссылка источника.
Ответ 2
Если вы пытаетесь максимизировать пропускную способность для задач с привязкой к IO, вы абсолютно должны комбинировать традиционные API асинхронной обработки (APM) с вашей работой на основе TPL. API APM - единственный способ разблокировать поток ЦП, пока асинхронный обратный вызов ввода-вывода не ожидается. TPL предоставляет вспомогательный метод TaskFactory::FromAsync
, чтобы помочь в объединении кода APM и TPL.
Ознакомьтесь с этим разделом .NET SDK в MSDN под названием TPL и традиционным асинхронным программированием .NET для получения дополнительной информации о том, как объединить эти два программирования для достижения асинхронной нирваны.
Ответ 3
Вы правы, что TPL удаляет часть вашего элемента управления, когда вы создаете свой собственный пул потоков. Но это правильно, если вы не хотите копать глубже. TPL позволяет создавать длительные задачи, которые не являются частью пула потоков TPL и могут хорошо служить вашей цели. Опубликованная книга, которая представляет собой бесплатное чтение Parallel Programming with Microsoft.NET, даст вам гораздо больше информации о том, как использовать TPL для использования.
У вас всегда есть возможность предоставить Paralle.For, Tasks explicit
параметры, сколько потоков должно быть выделено. Кроме того, вы можете заменить планировщик TPL своим собственным, если хотите получить полный контроль.
Ответ 4
Вы можете назначить собственный планировщик задач для задачи TPL. По умолчанию кража работы одна довольно умен.
Ответ 5
Я боюсь, что TPL даст похожие результаты для последовательного подхода к моему делу с привязкой к IO.
Думаю, что так и будет. Что такое узкое место? Является синтаксический анализ или загрузка? Многопоточность не поможет вам с загрузкой из Интернета.
Я бы использовал параллельную библиотеку задач для обрезки, применяя маску или эффекты для загруженных изображений, вырезая образец из подкаста и т.д. Он более масштабируемый.
Но это не будет порядок увеличения скорости. Потратьте ресурсы на реализацию некоторых функций, тестирование.
PS. "Ничего себе, моя функция выполняется в 0,7 с вместо 0,9";)
Ответ 6
Если вы распараллеливаете свои обращения к 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 correct to call wait
Разница между многопоточными и асинхронными в этом случае заключается в том, как выполняется обратный вызов/завершение.
При использовании EAP количество задач не связано с количеством потоков.
Поскольку вы полагаетесь на задачу GetAsync, клиент http использует сетевой поток (сокет, клиент tcp или что-то еще) и сигнализирует его, чтобы поднять событие при выполнении BeginRead/EndRead. Таким образом, ни один поток не участвует в этом моменте.
После вызова завершения, возможно, создается новый поток, но он работает с TaskScheduler (используется в вызове GetAsync/ContinueWith call), чтобы создать новый поток, использовать существующий поток или встроить задачу для использования вызывающего потока.
Если AnalyzeThisWords
блокирует слишком много времени, вы начинаете получать узкие места, поскольку "обратный вызов" в ContinueWith выполняется из рабочего пула потоков.