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

146

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

Ник Хайнер
источник
53
нам нравятся такие вопросы, они ставят под сомнение самые фундаментальные аспекты чего-то, что считается само собой разумеющимся… продолжайте идти ..
Шринивас Редди Татипарти
6
Когда в последний раз на вашем четырехъядерном компьютере одновременно работали Firefox, MS Word, Winamp, Eclipse и менеджер загрузок (более четырех программ / процессов)? Кроме того, одно приложение может иногда порождать более четырех потоков - как насчет этого?
Amarghosh
1
Воровство не обязательно плохо. У вас может быть поток с более высоким приоритетом для важных задач, которым нужно украсть время.
kichik 02
1
@Amarghosh Я предполагаю, что это был вопрос, почему одно приложение может захотеть порождать больше потоков, чем ядер, если это не дает никакого преимущества в производительности. И ваш пример с более чем четырьмя программами здесь не совсем актуален. Как вы правильно заметили, это процессы. Функция многозадачности ОС (мультиплексирование процессов) имеет мало общего с потоками внутри одного процесса.
Александр Иванников

Ответы:

86

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

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

На самом деле все сложнее:

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

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

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

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

Дэвид
источник
17
+1 за «но только если каждому отдельному потоку требуется 100% ЦП». Это было предположение, которое я не осознавал.
Ник Хайнер
53

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

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

Янтарь
источник
12
Хорошо сказано. Аргумент «один поток на ЦП» применяется только к коду, привязанному к ЦП. Асинхронное программирование - еще одна причина использовать потоки.
Джошуа Дэвис,
27

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

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

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

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

JustJeff
источник
1
Это интересная мысль ... Я всегда слышал, что многопоточность приложения - это чистое добавление сложности, но то, что вы говорите, имеет смысл.
Ник Хайнер,
Многопоточность приложения добавляет сложности, если его задачи не разделены должным образом. Если он разработан с минимальным перекрытием проблем (и, следовательно, с общим состоянием), это чистая экономия на проблемах сложности.
ТОЛЬКО МОЕ ПРАВИЛЬНОЕ МНЕНИЕ
Есть способы структурировать однопоточные приложения, чтобы поток управления был более понятным на уровне написания программ. OTOH, если вы можете структурировать свои потоки так, чтобы они передавали только сообщения друг другу (вместо того, чтобы иметь общие ресурсы), тогда довольно просто понять, что происходит, и заставить все работать.
Donal Fellows
1
Однако следует отметить, что использование потоков может упростить задачу только до определенного момента. Слишком часто делается попытка заставить два потока выполнять ту работу, которую должным образом должен выполнять один, и при этом сложность возвращается в пиках. Симптомами этого являются чрезмерная потребность в общении и синхронизации для согласования желаемого результата.
JustJeff
17

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

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

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

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

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

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

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

IceArdor
источник
то же самое можно сделать с одним потоком и очередью: \ действительно ли есть какая-то выгода от наличия 80 потоков на 2-4 ядрах, по сравнению с просто наличием 2-4 ядер, которые просто съедают задачи из очереди, как только они прибывают а им делать нечего?
Дмитрий
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

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

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

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

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

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

ТОЛЬКО МОЕ ПРАВИЛЬНОЕ МНЕНИЕ
источник
7

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

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

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

Кулачок
источник
Обработка пользовательского интерфейса - классический пример задачи, связанной с вводом-выводом. Нехорошо иметь одно ядро ​​ЦП, выполняющее и обработку, и задачи ввода-вывода.
Donal Fellows
6

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

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

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

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

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

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

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

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

рыба
источник
5
Если у вас много потоков, привязанных к процессору , то идеальное количество - один на процессор (или, возможно, на один меньше, чтобы оставить один для управления всеми операциями ввода-вывода, ОС и всем остальным). Если у вас есть потоки, связанные с вводом-выводом , вы можете довольно много складывать на одном процессоре. В разных приложениях разное сочетание задач, связанных с процессором и вводом-выводом; это совершенно естественно, но почему вы должны быть осторожны с универсальными декларациями.
Donal Fellows
1
Конечно, наиболее важным различием между потоками и процессами является то, что в Windows нет fork (), поэтому создание процесса действительно дорого, что приводит к чрезмерному использованию потоков.
ninjalj
За исключением сворачивания белков, SETI и т. Д. Не существует практических пользовательских задач, требующих длительного вычисления. Всегда есть потребность получить информацию от пользователя, поговорить с диском, поговорить с СУБД и т. Д. Да, расходы на fork () - одна из многих вещей, которые Катлер проклинал NT, о чем знали другие в DEC.
Fishtoprecords
5

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

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

Tobiw
источник
2
Вы пишете программное обеспечение для операционной системы, которая поддерживает многопоточность, но не имеет возможности мультиплексировать io? Я думаю, что веб-сервер, вероятно, является плохим примером, поскольку в этом случае мультиплексирование io почти всегда будет более эффективным, чем создание большего количества потоков, чем ядер.
Джейсон Коко,
3

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

Щенок
источник
Наверняка. просто используйте диспетчер задач в Windows или TOP в реальной ОС и посмотрите, сколько задач / процессов уже выполняется. Его всегда 90% или больше.
Fishtoprecords
2

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

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

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

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

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

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

Шрикар Додди
источник
2
Я вообще не понимаю, как это решает его вопрос. Моя интерпретация его вопроса связана с использованием потоков ядер и оптимальным использованием доступных ресурсов, или о поведении потоков при увеличении их числа, или о чем-то подобном в любом случае.
Дэвид
@ Дэвид, возможно, это не был прямой ответ на мой вопрос, но я все же чувствую, что узнал, прочитав его.
Ник Хайнер
1

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

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

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

Kyoryu
источник
Использование потоков для периодических задач - очень распространенный и желанный рабочий процесс, и было бы гораздо хуже, если бы они украли ядро.
Ник Бастин,
@Nick Bastin: Да, но эффективнее помещать эти задачи в очередь задач и выполнять их из этой очереди (или аналогичной стратегии). Для оптимальной эффективности 1 поток на ядро ​​превосходит все, поскольку он предотвращает накладные расходы из-за ненужного переключения контекста и выделения дополнительных стеков. Независимо от того, что периодическая задача должна украсть ядро, пока она «активна», поскольку ЦП может фактически выполнять только одну задачу на ядро ​​(плюс такие вещи, как гиперпоточность, если она доступна).
kyoryu
@Nick Bastin: К сожалению, как я уже сказал в основном ответе, большинство современных языков не подходят для простой реализации системы, которая делает это эффективно, нетривиально - вы в конечном итоге боретесь с типичным использованием языка.
kyoryu
Я хочу сказать не о том, что один поток на ядро ​​не является оптимальным, а то, что один поток на ядро ​​- это несбыточная мечта (если только вы не встроены), и проектирование, чтобы попытаться его реализовать, - пустая трата времени, так что вы также можете делайте то, что вам проще (и в любом случае не менее эффективно в современном планировщике), а не пытайтесь оптимизировать количество используемых вами потоков. Должны ли мы раскручивать темы без уважительной причины? Конечно, нет, но неважно, тратите ли вы ресурсы компьютера напрасно, независимо от потоковой передачи.
Ник Бастин
@Nick Bastin: Итак, подведем итоги: один поток на ядро ​​- это идеальный вариант, но на самом деле это маловероятно. Я, вероятно, должен был быть сильнее, чем «несколько сложнее», когда говорил о том, насколько вероятно, что я действительно достигну такого результата.
kyoryu
1

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

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

L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳
источник
1

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

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

Джай
источник
0

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

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

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

Джо Снайдер
источник
-4

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

Карлп
источник
Я мог бы отредактировать это, добавив больше примеров после отрицательных голосов, но поток (или процесс, в этом контексте почти никакой разницы) был изобретен не для увеличения производительности, а для упрощения асинхронного кода и избежания написания сложных конечных автоматов. который должен был обрабатывать все возможные в программе суперсостояния. Фактически даже на больших серверах обычно был один процессор. Мне просто интересно, почему мой ответ считается антиполезным?
KarlP
-8

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

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

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

R .. GitHub НЕ ПОМОГАЕТ ICE
источник
7
Не (совсем!) Стоит -1, но если серьезно, это одна из самых глупых ехидных вещей, которые мне приходилось слышать по этому поводу. У меня, например, нет проблем с реализацией конечного автомата. Вовсе нет. Я просто не люблю их использовать, когда есть другие инструменты, которые оставляют позади более понятный и простой в поддержке код. У государственных машин есть свои места, и в этих местах они не могут сравниться. Чередование операций, интенсивно использующих ЦП, с обновлениями графического интерфейса - не одно из таких мест. По крайней мере, там лучше подходят корутины, а еще лучше - многопоточность.
ТОЛЬКО МОЕ ПРАВИЛЬНОЕ МНЕНИЕ
Для всех, кто изменяет мой ответ, это НЕ аргумент против использования потоков! Если вы можете запрограммировать конечный автомат, это прекрасно и, конечно, часто имеет смысл запускать конечный автомат в отдельных потоках, даже если вам это не нужно. Мой комментарий заключался в том, что часто выбор в пользу использования потоков делается в первую очередь из желания избежать проектирования конечных автоматов, которые многие программисты считают «слишком сложными», а не ради каких-либо других преимуществ.
R .. GitHub НЕ ПОМОГАЕТ ICE