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

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

Ответ 1

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

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

Ответ 2

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

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

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

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

  • Редко для потока действительно нужен 100% процессор. Например, когда он использует дисковый или сетевой ввод-вывод, он может потенциально тратить время, не делая ничего полезного. Это очень распространенная ситуация.

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

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

Ответ 3

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

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

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

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

Ответ 4

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

Рассмотрим поток, который должен считывать данные с жесткого диска. В 2014 году типичное процессорное ядро ​​работает на частоте 2,5 ГГц и может выполнять 4 команды за цикл. При времени цикла 0,4 нс процессор может выполнять 10 команд на наносекунду. При типичном времени поиска жесткого диска около 10 миллисекунд процессор способен выполнить 100 миллионов инструкций за время, необходимое для считывания значения с жесткого диска. Могут быть значительные улучшения производительности с жесткими дисками с небольшим кешем (4 МБ-буфера) и гибридными дисками с несколькими ГБ хранилища, поскольку латентность данных для последовательных чтений или чтения из гибридного раздела может быть на несколько порядков быстрее.

Ядро процессора может переключаться между потоками (стоимость приостановки и возобновления потока составляет около 100 тактов), в то время как первый поток ожидает ввода с высокой задержкой (что-нибудь более дорогое, чем регистры (1 такт) и ОЗУ (5 наносекунд) ) К ним относятся дисковый ввод-вывод, сетевой доступ (латентность 250 мс), чтение данных с компакт-диска или медленная шина или вызов базы данных. Имея больше потоков, чем ядра, полезная работа может быть выполнена при выполнении задач с высокой задержкой.

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

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

Это также объясняет, почему важное значение имеет предсказание ветки. Если у вас есть оператор if, который требует загрузки значения из ОЗУ, но тело операторов if и else использует значения, уже загруженные в регистры, процессор может выполнить одну или обе ветки до того, как условие будет оценено. Как только условие вернется, процессор применит результат соответствующей ветки и отбросит другую. Выполнение потенциально бесполезной работы здесь, вероятно, лучше, чем переход на другой поток, который может привести к измельчению.

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

Ответ 5

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

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

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

Ответ 6

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

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

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

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

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

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

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

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

Ответ 7

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

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

Ответ 8

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

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

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

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

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

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

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

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

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

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

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

Ответ 9

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

Ответ 10

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

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

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

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

Ответ 11

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

Ответ 12

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

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

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

Ответ 13

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

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

Ответ 14

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

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

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

Ответ 15

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

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

Ответ 16

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

Ответ 17

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

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

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