2009-04-30 3 views
217

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

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

+0

Есть ли разница с Linux 2.4? – mouviciel

+2

Разница между процессами и потоками в Linux 2.4 заключается в том, что потоки разделяют больше частей своего состояния (адресное пространство, дескрипторы файлов и т. Д.), Чем процессы, которых обычно нет. NPTL под Linux 2.6 делает это немного яснее, предоставляя им «группы потоков», которые немного похожи на «процессы» в win32 и Solaris. – MarkR

+0

Да, NPTL хорош: он делает такие вещи, как kill, exec и т. Д., Работает так, как вы ожидали бы в многопоточной программе (старое поведение LinuxThreads имеет смысл с учетом реализации, но было неприятным).OTOH «группа потоков» - это всего лишь коллекция «потоков», и на самом деле она не занимает самих ресурсов, поэтому она весит тонну, чем процесс NT или Solaris. – ephemient

ответ

3

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

-1

Если вам нужно поделиться ресурсами, вам действительно нужно использовать потоки.

Также рассмотрите тот факт, что контекст переключается между потоками намного дешевле, чем контекстные переключатели между процессами.

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

+2

У меня есть репутация для редактирования, но я не совсем согласен. Контекстные переходы между процессами в Linux почти * столь же дешевы, как контекст, переключаются между потоками. – ephemient

48

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

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

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

+6

Ответ Адама будет служить в качестве исполнительного брифинга. Для более подробной информации MarkR и ephemient дают хорошие объяснения. Очень подробное объяснение с примерами можно найти по адресу: http://www.cs.cf.ac.uk/Dave/C/node29.html , но оно немного устарело. – CyberFonic

+0

CyberFonic подходит для Windows. Как говорит ephemient, в Linux процессы не тяжелее. И под Linux все механизмы, доступные для связи между потоками (futex, shared memory, pipe, IPC), также доступны для процессов и работают с одинаковой скоростью. –

3

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

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

4

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

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

9

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

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

55

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

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

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

Вариант 2 - нити

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

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

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

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

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

  1. конфигурации и среды исполнения данных Read
  2. Подавать затребован

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

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

+5

@ Qwertie forking - это не круто, он разбивает множество библиотек тонким способом (если вы используете их в родительском процессе). Это создает неожиданное поведение, которое смущает даже опытных программистов. – MarkR

+1

@MarkR вы могли бы привести несколько примеров или ссылку на то, как forking ломает библиотеку и создает неожиданное поведение? –

+15

Если процесс вилки с открытым подключением mysql, происходят плохие вещи, поскольку сокет разделяется между двумя процессами. Даже если только один процесс использует соединение, другой останавливает его закрытие. – MarkR

275

В 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 syscalls, но в ядре sys_fork и sys_clone - оба очень тонкие обертки вокруг той же самой функции do_fork, которая сама является тонкой оберткой вокруг copy_process. Да, термины process, thread и task используются как взаимозаменяемые, а в ядре Linux ...

+4

Я думаю, что нам не хватает 1 балла. Если вы делаете несколько процессов для своего веб-сервера, вам нужно написать еще один процесс, чтобы открыть сокет и передать «работу» в разные потоки. Threading предлагает один процесс с несколькими потоками, чистый дизайн. Во многих ситуациях поток является естественным, а в другой ситуации новый процесс является естественным. Когда проблема падает в серой области, важны другие компромиссы, объясняемые эфемерным. – Saurabh

+20

@Saurabh Не совсем. Вы можете легко использовать 'socket',' bind', 'listen',' fork', а затем подключать несколько процессов 'accept' в одном и том же прослушивающем сокете. Процесс может перестать принимать, если он занят, и ядро ​​будет направлять входящие соединения на другой процесс (если никто не слушает, ядро ​​будет стоять в очереди или отбрасывать, в зависимости от 'listen' backlog). У вас нет гораздо большего контроля над распределением работы, чем это, но обычно это достаточно хорошо! – ephemient

+0

«данные могут быть уже загружены в кеш» - какой кеш точно? – n611x007

3

Чтобы еще более усложнить, есть такая вещь, как thread-local storage и Unix общей памяти.

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

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

+1

Я использовал потоковое локальное хранилище для сбора статистики, в последний раз, когда я писал программу с потоковыми сетями: каждый поток писал свои собственные счетчики, не требовал блокировок, и только тогда, когда messaged каждый поток объединял свою статистику в глобальные итоги , Но да, TLS не очень часто используется или необходимо. Совместная память, с другой стороны ... помимо эффективной отправки данных вы также можете делиться семафорами POSIX между процессами, помещая их в общую память. Это потрясающе. – ephemient

8

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

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

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

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

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

+1

Да, у меня есть некоторые данные. Я проверил тест, который создает 100 000 процессов и тест, который создает 100 000 потоков. Версия потока работает примерно в 9 раз быстрее (17,38 секунды для процессов, 1,93 для потоков). Теперь это только проверяет время разработки, но для краткосрочных задач время создания может быть ключевым. – user17918

+4

@ user17918 - Возможно ли, чтобы вы делились используемым вами кодом для расчета вышеупомянутых таймингов. – codingfreak

+0

один большой, с процессами ядро ​​создает таблицу страниц для каждого процесса, а theads используют только таблицы с одной страницей, поэтому я думаю, что это нормальные потоки быстрее, чем процессы – c4f4t0r

2

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

+0

Что значит не польза? Как насчет выполнения больших вычислений в потоке графического интерфейса? Перемещение их в параллельный поток будет намного лучше с точки зрения пользователя, независимо от загрузки процессора. – olegst

11

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

1> Все темы в программе должны выполняться одинаково. С другой стороны, дочерний процесс может запускать другой исполняемый файл, вызывая функцию exec.

2> Исправляемый поток может нанести вред другим потокам в том же процессе, потому что потоки разделяют одно и то же пространство виртуальной памяти и другие ресурсы. Например, запись дикой памяти через неинициализированный указатель в одном потоке может привести к повреждению памяти, видимой для другого потока.

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

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

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

6> Обмен данными между потоками является тривиальным, поскольку потоки разделяют одну и ту же память. (Тем не менее, следует проявлять большую осторожность, чтобы избежать условий гонки, как описано ранее.) Для обмена данными между процессами требуется использование механизмов МПК. Это может быть более громоздким, но делает несколько процессов менее подверженными ошибкам параллелизма.

+3

Этот ответ скопирован дословно из книги Advanced Linux Programming (опубликовано в 2001 году). – sup

2

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