Потоки против процессов в Linux - PullRequest
230 голосов
/ 30 апреля 2009

Недавно я слышал, как несколько человек говорят, что в Linux почти всегда лучше использовать процессы, а не потоки, поскольку Linux очень эффективен в обработке процессов и потому, что существует так много проблем (таких как блокировка), связанных с ними. с темами. Тем не менее, я подозрительно, потому что кажется, что потоки могут дать довольно большой прирост производительности в некоторых ситуациях.

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

Ответы [ 14 ]

300 голосов
/ 01 мая 2009

В Linux используется модель потоков 1-1, в которой (для ядра) нет различий между процессами и потоками - все это просто выполняемая задача. *

В Linux системный вызов clone клонирует задачу с настраиваемым уровнем общего доступа, среди которых:

  • CLONE_FILES: использовать одну и ту же таблицу дескрипторов файлов (вместо создания копии)
  • CLONE_PARENT: не устанавливать родительско-дочерние отношения между новой задачей и старой (в противном случае дочерняя getppid() = родительская getpid())
  • CLONE_VM: совместно использовать то же пространство памяти (вместо создания COW копия)

fork() звонки clone( наименьшая доля ) и pthread_create() звонки clone( наибольшая доля ). **

fork стоит чуть больше, чем pthread_create, из-за копирования таблиц и создания отображений COW для памяти, но разработчики ядра Linux постарались (и преуспели) в минимизации этих затрат.

Переключение между задачами, если они совместно используют одно и то же пространство памяти и различные таблицы, будет немного дешевле, чем если бы они не были общими, потому что данные уже могут быть загружены в кэш. Однако переключение задач по-прежнему происходит очень быстро, даже если ничего не передается - это то, что разработчики ядра Linux пытаются обеспечить (и добиваются успеха).

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


* Упрощено. CLONE_THREAD приводит к совместному использованию доставки сигналов (для этого требуется CLONE_SIGHAND, который разделяет таблицу обработчиков сигналов).

** Упрощенно. Существуют системные вызовы SYS_fork и SYS_clone, но в ядре оба sys_fork и sys_clone представляют собой очень тонкие оболочки для одной и той же функции do_fork, которая сама является тонкой оболочкой для copy_process. Да, термины process, thread и task используются в ядре Linux довольно взаимозаменяемо ...

57 голосов
/ 01 мая 2009

Linux (и, действительно, Unix) дает вам третий вариант.

Вариант 1 - процессы

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

Вариант 2 - темы

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

Вариант 3 - вилка

Доступно только в Linux / Unix, это немного отличается. Разветвленный процесс на самом деле является собственным процессом с собственным адресным пространством - дочерний элемент не может (обычно) ничего сделать, чтобы повлиять на адресное пространство своего родителя или братьев и сестер (в отличие от потока), поэтому вы получаете дополнительную надежность.

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

Рассмотрим программу веб-сервера, которая состоит из двух шагов:

  1. Чтение данных конфигурации и времени выполнения
  2. Запросы на обслуживание страниц

Если вы использовали потоки, шаг 1 будет выполнен один раз, а шаг 2 - в нескольких потоках. Если вы использовали «традиционные» процессы, шаги 1 и 2 должны были бы повторяться для каждого процесса, а память для хранения данных конфигурации и времени выполнения дублировалась. Если вы использовали fork (), то вы можете выполнить шаг 1 один раз, а затем fork (), оставив данные и конфигурацию времени выполнения в памяти без изменений, не скопировав.

Так что на самом деле есть три варианта.

50 голосов
/ 30 апреля 2009

Это зависит от множества факторов. Процессы более тяжелые, чем потоки, и имеют более высокую стоимость запуска и завершения работы. Межпроцессное взаимодействие (IPC) также сложнее и медленнее, чем межпоточное взаимодействие.

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

Таким образом, если модули вашего приложения могут работать в основном независимо при небольшом обмене данными, вам, вероятно, следует использовать процессы, если вы можете позволить себе затраты на запуск и завершение работы. Падение производительности IPC будет минимальным, и вы будете немного безопаснее от ошибок и брешей в безопасности. Если вам нужен каждый бит производительности, который вы можете получить или иметь много общих данных (таких как сложные структуры данных), используйте потоки.

10 голосов
/ 30 апреля 2009

Другие обсуждали соображения.

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

8 голосов
/ 18 мая 2009

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

В современном Linux (2.6.x) нет большой разницы в производительности между переключением контекста процесса по сравнению с потоком (только поток MMU является дополнительным для потока). Существует проблема с общим адресным пространством, что означает, что неисправный указатель в потоке может повредить память родительского процесса или другого потока в том же адресном пространстве.

Процесс защищен MMU, поэтому неисправный указатель просто вызовет сигнал 11 и не повредит.

В общем, я бы использовал процессы (не слишком много издержек при переключении контекста в Linux, но защита памяти из-за MMU), но я бы подумал, если мне понадобится класс планировщика реального времени, который представляет собой другую чашку чая.

Как вы думаете, почему потоки имеют такой большой прирост производительности в Linux? У вас есть данные для этого или это просто миф?

5 голосов
/ 30 апреля 2009

Насколько тесно связаны ваши задачи?

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

4 голосов
/ 01 мая 2009

Чтобы еще больше усложнить ситуацию, существует такая вещь, как локальное хранилище потоков и общая память Unix.

Локальное хранилище потоков позволяет каждому потоку иметь отдельный экземпляр глобальных объектов. Единственный раз, когда я использовал его, был при создании среды эмуляции в linux / windows для кода приложения, работающего в RTOS. В RTOS каждая задача была процессом с собственным адресным пространством, в среде эмуляции каждая задача была потоком (с общим адресным пространством). Используя TLS для таких вещей, как синглеты, мы смогли создать отдельный экземпляр для каждого потока, как в «реальной» среде RTOS.

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

3 голосов
/ 09 сентября 2015

В моей недавней работе с LINUX важно знать о библиотеках. Если вы используете потоки, убедитесь, что все библиотеки, которые вы можете использовать между потоками, являются поточно-ориентированными. Это обожгло меня пару раз. Примечательно, что libxml2 не поддерживает потоки из коробки. Он может быть скомпилирован с поддержкой потоков, но это не то, что вы получаете при установке aptitude.

3 голосов
/ 30 апреля 2009

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

Для реального примера веб-сервера apache 1.3 использовался только для поддержки нескольких процессов, но в 2.0 он добавил абстракцию , чтобы вы могли переключаться между ними. Комментарии кажется до согласны с тем, что процессы более устойчивы, но потоки могут дать немного лучшую производительность (за исключением окон, где производительность для процессов отстой, и вы хотите только использовать темы).

3 голосов
/ 30 апреля 2009

Я должен согласиться с тем, что вы слышали. Когда мы тестируем наш кластер (xhpl и т. Д.), Мы всегда получаем значительно лучшую производительность с процессами над потоками. </anecdote>

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