1.1 История появления
Человечество очень часто приходит к идее построить новый грандиозный проект, который затмит все предыдущие грандиозные проекты. Пирамида Хеопса в Гизе — самая большая, Бурдж Халифа в Дубае — самая высокая, а Великая Китайская стена — самая длинная.
Однако организовать работу над такими проектами очень сложно. Если строить новую пирамиду в два раза выше, то она потребует в восемь раз больше камня. Значит, нужно или поднять производительность каменоломен, или открыть больше карьеров.
Также нужно будет найти в восемь раз больше инженеров, возможно, открыть инженерные школы, стандартизировать чертежи, геометрию, письменность. Словом, та ещё задачка...
За прошедшие с момента строительства пирамид тысячи лет ничего не изменилось — люди продолжают думать, как выполнить всё больше работы за меньшее время. И когда изобрели компьютеры, над этой задачей стали биться лучшие умы человечества.
Как выполнить программу в десять раз быстрее? Казалось бы, странный вопрос — если процессор уже работает на максимальной скорости, то, наверное, никак. Однако я не зря упомянул лучшие умы человечества. Они проанализировали работу всех программ и пришли к выводу, что есть три больших направления для роста.
Устранение простоев
Оказывается, большую часть времени программа простаивает. Она постоянно чего-то ждёт: данные должны скопироваться из одного места памяти в другое, загрузиться с жёсткого диска в память, требуется дождаться ответа сервера на запрос, ввода данных от пользователя и т.д.
Все эти задачи выполняются не центральным процессором, а контроллерами памяти, диска и т.п. И центральный процессор в это время можно было бы нагрузить чем-то полезным. Так появилась идея запускать в одном процессоре не один поток выполнения команд thread, а несколько.
И пока, например, один поток ждёт ввода данных от пользователя, второй скачивает что-то по сети, третий обрабатывает данные, четвёртый отрисовывает на экран изображения. Затем эта задача эволюционирует в асинхронные задачи и корутины, но об этом позднее.
Больше программ
Если программы простаивают 80% времени, то это, конечно же, нехорошо. С другой стороны, нельзя же переписать все программы под наши новые подходы. Возможно, эту проблему можно решить иначе — можно просто запускать на компьютере несколько программ одновременно.
В этом случае операционная система следит за работой программ, и если программа простаивает, то передаёт её время выполнения другой программе. Такое переключение происходит десятки раз в секунду, и пользователь просто не замечает переключений — с его точки зрения программы выполняются одновременно.
Больше процессоров
Одновременное выполнение программ — это круто, но что, если программ много, а простаивают они мало? Нет простоев — нет эффективного использования. Например, у нас десять программ, которые что-то вычисляют, или ресурсоёмкая игра и ещё что-то вместе с ней.
Решением этой проблемы стало добавление в процессор нескольких процессоров. Чтобы избежать путаницы, их стали называть ядрами. Теперь у нас есть процессоры, у которых несколько ядер (суб-процессоров), на которых параллельно работают десятки программ.
Это интересно! Количество ядер в процессорах
На сегодняшний день серверные процессоры могут иметь от 64 до 256 ядер, а в некоторых специализированных случаях даже больше. Например, процессоры AMD EPYC 4-го поколения предлагают до 96 ядер, а IBM POWER10 может иметь до 240 ядер на чип. Пользовательские процессоры также значительно эволюционировали: высокопроизводительные настольные CPU, такие как AMD Threadripper, могут иметь до 64 ядер, в то время как более распространённые модели обычно имеют от 6 до 16 ядер.
Куда же дальше? Есть куда! Во-первых, в одну серверную материнскую плату можно установить несколько процессоров, например два или даже четыре. Во-вторых, серверы можно объединять в серверные стойки по 10–20 штук. А серверные стойки — в дата-центры, где таких стоек тысячи.
Распределённые системы и микросервисные архитектуры стали распространённой практикой в современной разработке программного обеспечения. Многие крупные компании, такие как Google, Amazon, Netflix, и даже меньшие предприятия, используют распределённые системы для обработки больших объёмов данных, обеспечения высокой доступности и масштабируемости своих сервисов. Эти системы позволяют эффективно использовать ресурсы множества серверов, работающих вместе как единое целое, что значительно повышает производительность и отказоустойчивость приложений.
1.2 Преимущества
Многопоточность, или одновременное выполнение задач внутри программы, предлагает несколько ключевых преимуществ, которые могут значительно улучшить производительность и эффективность программного обеспечения. Рассмотрим основные преимущества многопоточности.
1. Повышение производительности и скорости выполнения
Параллельное выполнение задач: Многопоточность позволяет одновременно выполнять несколько задач, что особенно полезно для программ, требующих выполнения множества независимых операций. Это позволяет ускорить выполнение программы, поскольку задачи выполняются параллельно, используя все доступные ресурсы процессора.
Использование многоядерных процессоров: Современные процессоры имеют несколько ядер, и многопоточность позволяет полностью использовать их мощность, распределяя потоки по различным ядрам для выполнения задач параллельно.
2. Улучшение отзывчивости и взаимодействия с пользователем
Фоновые задачи: Многопоточность позволяет выполнять длительные или ресурсоемкие операции в фоновом режиме, сохраняя при этом основной поток, отвечающий за пользовательский интерфейс, отзывчивым. Это улучшает пользовательский опыт, поскольку интерфейс не блокируется при выполнении тяжелых задач.
Асинхронные операции: Взаимодействие с пользователем и обработка событий могут происходить параллельно с выполнением основных задач, что делает приложения более отзывчивыми и эффективными.
3. Эффективное использование ресурсов системы
Оптимизация ресурсов: Многопоточность позволяет более эффективно использовать ресурсы системы, такие как процессорное время и память. Это особенно важно для серверов и высокопроизводительных вычислительных систем, где многопоточность позволяет обрабатывать большое количество запросов и задач одновременно.
Управление вводом-выводом: Многопоточные приложения могут управлять задачами ввода-вывода (например, сетевые операции, чтение и запись файлов) более эффективно, поскольку потоки могут выполнять другие задачи, пока одна из них ожидает завершения операции ввода-вывода.
4. Поддержка многозадачности
Многозадачность: Многопоточность позволяет выполнять несколько задач одновременно в рамках одного процесса, что упрощает разработку приложений, требующих многозадачности, таких как веб-серверы, базы данных и приложения реального времени.
Параллельная обработка данных: В задачах, связанных с обработкой больших объемов данных, многопоточность позволяет разбивать данные на части и обрабатывать их параллельно, что значительно ускоряет выполнение задачи.
Примеры использования многопоточности
Веб-серверы: Многопоточность позволяет веб-серверам обрабатывать множество клиентских запросов одновременно, повышая производительность и масштабируемость серверов.
Графические интерфейсы пользователя (GUI): В приложениях с графическим интерфейсом многопоточность позволяет выполнять длительные вычисления в фоновом режиме, сохраняя интерфейс отзывчивым.
Научные вычисления: Многопоточность используется для параллельной обработки данных в научных вычислениях, что позволяет значительно ускорить выполнение сложных вычислительных задач.
Игры и симуляции: В играх многопоточность позволяет одновременно обрабатывать физику, графику, звуки и действия пользователя, улучшая производительность и реализм.
1.3 Правильное название
К названию «многопоточность» есть претензии. Оно состоит из двух слов: «много» и «поток», как бы намекая на то, что внутри программы находится много «потоков выполнения команд», которые что-то выполняют.
Красивая аналогия, но в английской (оригинальной) литературе для обозначения нескольких параллельно выполняемых действий используется термин «нить» (thread). И, соответственно, многопоточность там звучит как multi-threading.
Это можно было бы считать мелким недоразумением — кому какое дело, как разные термины переводятся на другой язык, если бы в программировании не стали активно использовать такую вещь, как stream, которую кроме как словом «поток» не переведёшь.
Поэтому сейчас в русскоязычной терминологии существует некоторая путаница, которую решают двумя способами:
-
Thread(нить) переводят как «поток выполнения [команд]». Stream(поток) переводят как «поток данных».
С другой стороны, многие программисты просто начали использовать англоязычные термины без перевода:
-
Thread(нить) произносят как «трэд», а multi-threading как «малти-трэдинг». Stream(поток) произносят как «стрим».
Thread часто называют нитью, но термин «много-нитевость» так и не прижился. Поэтому часто в разговоре используют «нить» и «многопоточность» одновременно.
Использование большого количества заимствованных терминов делает язык богаче, позволяет наполнить слова новым смыслом и упрощает коммуникацию с коллегами из других стран. Я полностью за этот подход.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ