Различия в разных способах создания параллельных программ

В чем разница между:

  • Запуск нового потока
  • Использование TPL
  • Использование BackgroundWorker

Все это создает concurrency, но каковы различия между ними на низком уровне? Все ли 3 делают нитки?

Спасибо

Ответ 1

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

  • Запуск нового потока вручную:

    Это фактически создает новый поток в ОС. Ваш код будет выполнен в этом потоке.

  • Использование BackgroundWorker:

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

    Пул управляет количеством доступных потоков и внутренне создает и уничтожает потоки по мере необходимости в определенных пределах. Это полезно, потому что пул может иметь некоторые алгоритмы для оптимизации создания потоков. Создание потоков - довольно дорогостоящий процесс, поэтому, если это необходимо, пул потоков сохраняет потоки живыми и повторно использует их для будущих запросов. У вас может быть ограниченный контроль над пулом, указав минимальные/максимальные числа потоков и некоторые незначительные настройки вроде этого.

    Существуют другие способы непосредственного использования ThreadPool, такие как QueueUserWorkItem (...).

  • Использование параллельной библиотеки задач:

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

    Например, если задано 100 задач, на четырехъядерном ядре TPL может появиться 4 потока, но на 8 ядре он может появиться 8 и распределить задачи по доступным потокам по мере завершения каждой задачи.

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

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

Ответ 2

  • Запуск новой темы является самым дорогим из трех, но вы получаете больше возможностей. Как настройка модели квартиры и приоритета.

  • Задача Параллельная библиотека запускает задачу в ThreadPool (если задача не отмечена как необходимая для ее собственного Thread) и добавляет функции обработки исключений и ожидания для завершения.

  • BackgroundWorker полезен только для запуска задач, созданных из WinForms или WPF. Он также использует Threadpool

  • Дополнительно: ThreadPool. Используется для выполнения коротких задач без накладных расходов при создании отдельного потока.

Ответ 3

Создание потока - это просто... вы создаете один поток, который запускается одновременно с основным потоком процесса.

В TPL, если вы создаете задачу, он будет использовать пул потоков, чтобы найти свободный поток для запуска задачи. Это может быть более эффективным, когда вы создаете множество задач, потому что TPL может балансировать нагрузку на любое количество бесплатных потоков (по-видимому, количество нитей сбалансировано на основе # ядра, которое у вас есть, но я не знаю этого точно. )

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

В конце концов, вам нужно выбрать то, что подходит вашей программе, но цель TPL Tasks - позволить вам эффективно планировать задачи, которые могут выполняться параллельно, тогда как создание потоков или использование фоновых работников может быть лучше для долгого выполняемые операции или сценарии, в которых вы хотите, чтобы фоновый поток постоянно находился в ожидании какого-либо сигнала (через я я бы предположил использовать RegisteredWait, если вы просто ожидаете какого-либо события для сигнала.)