Многопоточность: Какой смысл большего количества потоков, чем ядра?

Я думал, что точка многоядерного компьютера - то, что он мог выполнить несколько потоков одновременно. В этом случае, если у Вас есть четырехъядерная машина, какой смысл того, чтобы иметь больше чем 4 потока, работающие за один раз? Они только украли бы время друг от друга?

128
задан ROMANIA_engineer 7 December 2014 в 20:29
поделиться

14 ответов

На самом деле идеальное использование потоков - по одному на ядро.

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

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

1
ответ дан 24 November 2019 в 00:40
поделиться

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

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

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

Надеюсь, эти определения и дальнейшие исследования с использованием этих основ помогут вам понять.

2
ответ дан 24 November 2019 в 00:40
поделиться

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

Например, если вам нужно выполнить некоторую обработку в фоновом режиме и при этом не терять отклик на ввод пользовательского интерфейса, вы можете использовать потоки. Без потоков пользовательский интерфейс зависал бы каждый раз, когда вы пытались выполнить какую-либо тяжелую обработку.

Также см. Этот связанный вопрос: Практическое использование потоков

6
ответ дан 24 November 2019 в 00:40
поделиться

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

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

53
ответ дан 24 November 2019 в 00:40
поделиться

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

2
ответ дан 24 November 2019 в 00:40
поделиться

Я категорически не согласен с утверждением @ kyoryu о том, что идеальное число - один поток на процессор.

Подумайте об этом так: зачем у нас многопроцессорные операционные системы? На протяжении большей части компьютерной истории почти все компьютеры имели один центральный процессор. Однако с 1960-х годов все «настоящие» компьютеры имели многозадачные операционные системы.

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

давайте оставим в стороне споры о том, были ли версии Windows до NT многозадачными. С тех пор каждая реальная ОС имела многозадачность. Некоторые не раскрывают его пользователям, но они все равно там, например, слушают радио мобильного телефона, разговаривают с чипом GPS, принимают ввод от мыши и т. Д.

Потоки - это просто задачи, которые немного более эффективны. Нет принципиальной разницы между задачей, процессом и потоком.

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

Я согласен с тем, что для большинства процедурных языков, таких как C, C ++, Java и т. Д., Написание правильного поточно-безопасного кода - это большая работа. Учитывая, что сегодня на рынке представлены 6-ядерные процессоры, а неподалеку - 16-ядерные, я ожидаю, что люди отойдут от этих старых языков, поскольку многопоточность становится все более и более критическим требованием.

Несогласие с @kyoryu - это просто ИМХО, остальное - факт.

6
ответ дан 24 November 2019 в 00:40
поделиться

Ответ вращается вокруг цели потоков, то есть параллелизма: запускать несколько отдельных строк выполнения одновременно . В «идеальной» системе у вас будет один поток, выполняющийся на каждое ядро: без прерывания. На самом деле это не так.Даже если у вас есть четыре ядра и четыре рабочих нитей, ваш процесс, и потоки будут постоянно переключаясь из других процессов и потоков. Если вы работаете в любой современной операционной системы, каждый процесс имеет по меньшей мере одну нить, и многие из них больше. Все эти процессы выполняются одновременно. Вы, наверное, несколько сот тема все запущенные на вашей машине прямо сейчас. Вы никогда не получите ситуацию, когда поток проходит без времени «украденного» от него. (Ну, вы могли бы, если это работает в реальном времени , если вы используете ОС реального времени или даже на Windows, используйте приоритет реального времени потоков. Но это редко.)

С этим в качестве фона, ответ: Да, более чем в четыре нити на истинную четыре-сердечнике машины может дать вам такую ​​ситуацию, когда они «крадут время друг от друга, , но только если каждый отдельный поток должен 100% CPU . Если поток не работает 100% (как поток UI не может быть, или какой-нить делает небольшое количество работы или ожидания чего-то еще), то другой поток быть запланировано на самом деле хорошая ситуация.

Это на самом деле более сложным, чем это:

  • Что делать, если у вас есть пять бит работы, что все нужно делать сразу? Это имеет смысл, чтобы запустить их все сразу, чем запустить четыре из них, а затем запустить пятый позже.

  • Это редкий для нити, чтобы действительно нуждается 100% CPU. В тот момент он использует диск или сетевого ввода / вывода, например, это может быть потенциально тратить время на ожидание не делает ничего полезного. Это очень распространенная ситуация.

  • Если у вас есть работа, которая должна быть запущена, один общий механизм является использование ThreadPool.Может показаться, что имеет смысл иметь такое же количество потоков, как и количество ядер, но пул потоков .Net имеет до 250 потоков, доступных на процессор . Я не уверен, почему они это делают, но предполагаю, что это связано с размером задач, которые выполняются в потоках.

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

71
ответ дан 24 November 2019 в 00:40
поделиться

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

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

В ответ на ваш второй вопрос: большинство процессов / потоков не привязаны к ЦП (т. Е. Не работают непрерывно и непрерывно), а вместо этого останавливаются и часто ждут завершения ввода-вывода. Во время этого ожидания другие процессы / потоки могут работать без «воровства» кода ожидания (даже на одноядерной машине).

0
ответ дан 24 November 2019 в 00:40
поделиться

Некоторые API спроектированы так, что у вас нет другого выбора , кроме как запускать их в отдельном потоке (что угодно с блокирующими операциями). Примером могут служить HTTP-библиотеки Python (AFAIK).

Обычно это не большая проблема (если это проблема, ОС или API должны поставляться с альтернативным асинхронным режимом работы, например: select (2) ), потому что это, вероятно, означает, что поток будет спать во время ожидания завершения ввода-вывода. С другой стороны, если что-то выполняет тяжелые вычисления, у вас есть , чтобы поместить это в отдельный поток, чем, скажем, поток GUI (если вам не нравится ручное мультиплексирование).

1
ответ дан 24 November 2019 в 00:40
поделиться

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

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

5
ответ дан 24 November 2019 в 00:40
поделиться

Большинство ответов выше говорят о производительности и одновременной работе. Я собираюсь подойти к этому вопросу с другой стороны.

Возьмем, скажем, простую программу эмуляции терминала. Вы должны делать следующие вещи:

  • следить за входящими символами от удаленной системы и отображать их
  • следить за символами, поступающими с клавиатуры, и отправлять их в удаленную систему

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

Теперь цикл для чтения с пульта прост, согласно следующему псевдокоду:

while get-character-from-remote:
    print-to-screen character

Цикл для мониторинга клавиатуры и отправки также прост:

while get-character-from-keyboard:
    send-to-remote character

Проблема, однако, в том, что вы должны делать это одновременно. Теперь код должен выглядеть примерно так, если у вас нет потоковой обработки:

loop:
    check-for-remote-character
    if remote-character-is-ready:
        print-to-screen character
    check-for-keyboard-entry
    if keyboard-is-ready:
        send-to-remote character

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

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

Так почему же вы не используете потоковую обработку?

Ну, если ваши задачи привязаны к процессору, а не к вводу/выводу, потоковая обработка фактически замедляет работу системы. Производительность будет страдать. Во многих случаях очень сильно. ("Сбивание" - это распространенная проблема, если вы бросаете слишком много потоков, связанных с процессором. В итоге вы тратите больше времени на смену активных потоков, чем на выполнение содержимого самих потоков.) Кроме того, одна из причин простоты приведенной выше логики заключается в том, что я намеренно выбрал упрощенный (и нереалистичный) пример. Если вы захотите выводить на экран эхо того, что было набрано, то вы получите новый мир проблем, поскольку вы введете блокировку общих ресурсов. При наличии только одного разделяемого ресурса это не такая уж большая проблема, но она начинает становиться все более и более серьезной, когда у вас появляется больше ресурсов для совместного использования.

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

5
ответ дан 24 November 2019 в 00:40
поделиться

Многие потоки будут спать, ожидая пользовательского ввода, ввода/вывода и других событий.

3
ответ дан 24 November 2019 в 00:40
поделиться

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

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

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

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

26
ответ дан 24 November 2019 в 00:40
поделиться

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

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

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

-8
ответ дан 24 November 2019 в 00:40
поделиться
Другие вопросы по тегам:

Похожие вопросы: