Аннотация: В лекции рассматриваются понятие потока (thread) и многопоточное выполнение (multi-threading); модели многопоточности; пользовательские потоки и потоки ядра; потоки в «Эльбрусе», Solaris, Linux, POSIX, Windows 2000, Java.
Презентацию к данной лекции Вы можете скачать здесь.
Введение
Многопоточность (multi- threading ) – одна из наиболее интересных и актуальных тем в данном курсе и, по-видимому, в области ИТ вообще, и, кроме того, одна из излюбленных тем автора. Актуальность данной темы особенно велика, в связи с широким распространением многоядерных процессоров. В лекции рассмотрены следующие вопросы:
- Исторический обзор многопоточности
- Модели многопоточного исполнения
- Проблемы, связанные с потоками
- Потоки в POSIX (Pthreads)
- Потоки в Solaris 2
- Потоки в Windows 2000/XP
- Потоки в Linux
- Потоки в Java и .NET.
Однопоточные и многопоточные процессы
К сожалению, до сих пор мышление многих программистов при разработке программ остается чисто последовательным. Не учитываются широкие возможности параллелизма , в частности, многопоточности. Последовательный (однопоточный) процесс – это процесс, который имеет только один поток управления ( control flow ), характеризующийся изменением его счетчика команд . Поток (thread) – это запускаемый из некоторого процесса особого рода параллельный процесс, выполняемый в том же адресном пространстве, что и процесс-родитель. Схема организации однопоточного и многопоточного процессов изображена на рис. 10.1.
Что такое многопоточность/multithreading? Введение в процессы и потоки
Рис. 10.1. Однопоточный и многопоточный процессы.
Как видно из схемы, однопоточный процесс использует, как обычно, код, данные в основной памяти и файлы, с которыми он работает. Процесс также использует определенные значения регистров и стек , на котором исполняются его процедуры. Многопоточный процесс организован несколько сложнее. Он имеет несколько параллельных потоков, для каждого из которых ОС создает свой стек и хранит свои собственные значения регистров. Потоки работают в общей основной памяти и используют то же адресное пространство , что и процесс-родитель, а также разделяют код процесса и файлы.
Многопоточность имеет большие преимущества:
- Увеличение скорости (по сравнению с использованием обычных процессов). Многопоточность основана на использовании облегченных процессов (lightweight processes),работающих в общем пространстве виртуальной памяти. Благодаря многопоточности, не возникает больше неэффективных ситуаций, типичных для классической системы UNIX, в которой каждая команда shell (даже команда вывода содержимого текущей директории ls исполнялась как отдельный процесс, причем в своем собственном адресном пространстве. В противоположность облегченным процессам, обычные процессы (имеющие собственное адресное пространство) часто называют тяжеловесными (heavyweight).
- Использование общих ресурсов. Потоки одного процесса используют общую память и файлы.
- Экономия. Благодаря многопоточности, достигается значительная экономия памяти, по причинам, объясненным выше. Также достигается и экономия времени, так как переключение контекста на облегченный процесс, для которого требуется только сменить стек и восстановить значения регистров, значительно быстрее, чем на обычный процесс (см. «Методы взаимодействия процессов» ).
Использование мультипроцессорных архитектур. Это особенно важно в настоящее время, в период широкого использования многоядерных гибридных и многопроцессорных систем. Именно многопоточность программ, основанная на многоядерности процессора, дает возможность, наконец, почувствовать реальные преимущества параллельного выполнения.
МНОГОПОТОЧНОСТЬ НА PYTHON | МОДУЛЬ THREADING
История многопоточности
Как небезынтересно отметить, один из первых шагов на пути к широкому использованию многопоточности, по-видимому, был сделан в 1970-е годы советскими разработчиками компьютерной аппаратуры и программистами. МВК «Эльбрус-1», разработанный в 1979 году, поддерживал в аппаратуре и операционной системе эффективную концепцию процесса, которая была близка к современному понятию облегченного процесса. В частности, процесс в «Эльбрусе» однозначно характеризовался своим стеком. Иначе говоря, все процессы были облегченными и исполнялись в общем пространстве виртуальной памяти – других процессов в «Эльбрусе» просто не было!
Концепция многопоточности начала складываться, по-видимому, с 1980-х гг. в системе UNIX и ее диалектах. Наиболее развита многопоточность была в диалекте UNIX фирмы AThttps://intuit.ru/studies/courses/641/497/lecture/11284″ target=»_blank»]intuit.ru[/mask_link]
Зачем нужна многопоточность? Основа и теория.
Многопоточность нужна, чтобы мы могли параллельно решать несколько задач внутри приложения. Например, показывать анимацию солнышка, запрашивать данные о температуре с сервера и проигрывать звуки птиц.
Но начнём с основ. Существуют два очень похожих понятия: процессы и потоки. И многие их путают в самом начале изучения, включая меня. Но на самом деле всё просто. Процесс → отдельное приложение.
Если запущено 2 приложения, то это два разных процесса. Три приложения →
3 процесса. У каждого приложения свой процесс и у них нет доступа к соседнему процессу. Т.е. у каждого процесса какая-то своя область памяти, с которой он работает.
Потоки → это уже внутри приложения (процесса). Т.е. анимация солнышка и звуки птиц → это как раз потоки, которые происходят внутри одного процесса (приложения). Один поток отвечает за солнышко, второй за звуки, третий за запрос данных с сервера. И потоки могут обмениваться данными с другими потоками внутри приложения.
Главное запомнить: 1 процесс → 1 приложение, 1 приложение → много потоков.
Минутка истории : когда-то давно существовали компьютеры только с одним процессором. И вот они в единицу времени выполняли одну задачу. Если мы одновременно запускали Paint и Winamp (ох, уж эта ностальгия), то казалось, что мы одновременно рисуем и слушаем музыку. Ничего же не прерывалось. Но на самом деле всё выполнялось последовательно, просто очень-очень быстро.
Так быстро, что мы даже не улавливали это. Процессор постоянно переключался между музыкой и кисточкой в Paint. Но вот если параллельно запустится мощный антивирус, то всё зависнет. Как же я ненавидела те моменты. Просто в этот момент процессор отдавал время антивирусу, поэтому Winamp и Paint стояли в очереди.
С двумя ядрами стало легче, ведь можно было делать две параллельные задачи. Теперь антивирусу можно было отдать одно ядро, а на втором рисовать и слушать музыку. Так не зависает.
С многоядерностью вообще легче. Во многих учебниках все показывают пример с картинкой: есть изображение и надо пройтись по каждому пикселю и обесцветить его. Предположим, что это займёт минуту на 1 ядре. Т.е. просто последовательно проверяем каждый пиксель.
А теперь возьмём компьютер с 4 ядрами. Расчет каждого пикселя не зависит от другого. Значит эту задачу можно поделить на все ядра. Но стоит учесть, что сначала пара секунд уйдёт на раздел картинки (кто какой частью занимается) и пара секунд в конце на слияние картинки обратно в единое целое. Но зато теперь обесцветить всю картинку можно будет за 15 секунд + 4 = 19 секунд.
Значительная разница: 19 секунд против минуты. А представьте, что таких картинок много.
Обратите внимание, что четырехкратной скорости нет, потому что уходит время на подготовку и т.п.
Кстати, в играх аналогично задачи делятся на сегменты. И чем больше ядер, чем меньше тормозов: 1 ядро может заняться графикой, второе подгрузкой данных, третье ещё чем-нибудь. Красота. 🙂
Источник: dzen.ru
Многопоточность в Java
Прежде, чем узнать про потоки Java, давайте заглянем в недалёкое будущее. Представьте, что вы подали резюме и прошли собеседование. Вас и пару дюжин будущих коллег пригласили на работу в большую Software-компанию. Среди прочих хлопот нужно подать бумажные документы для трудоустройства уставшему сотруднику HR-отдела.
Чтобы ускорить процесс, претендентов на должность можно разделить на две группы и распределить их между двумя HR-менеджерами (если таковые есть в компании). В результате мы получаем ускорение процесса за счёт параллельной (parallel) работы по оформлению.
Если же кадровик в компании один, то придётся как-то выкручиваться. Например, можно снова- таки разбить всех на две группы, например, собеседовать поочерёдно девушек и юношей.
Или по другому принципу: так как в нижней группе больше народа, будем чередовать на одного юношу двух девушек.
Такой способ организации работы называется многопоточным. Наш утомлённый кадровик переключается на разные группы для оформления из них очередного сотрудника. Групп, может быть, одиннадцать, а кадровиков – четыре. В этом случае многопоточная (multithreading) обработка будет происходить параллельно несколькими HR-ами, которые могут брать очередного человека из любой группы для обработки его документов.
Процессы
Процессом (process) в данном случае будет организация работы приёма документов. В организации можно выделить несколько процессов: бухгалтерский учёт, разработка ПО, встречи с клиентами, работа склада и т. д. На каждый процесс выделены ресурсы: помещение, сотрудники для его исполнения. Процессы изолированы друг от друга: у кадровиков отсутствует доступ в бухгалтерскую базу, а менеджеры по работе с клиентами не бегают по складу. Если процесс должен получить доступ к чужим ресурсам, необходимо наладить межпроцессное взаимодействие: служебные записки, совместные совещания.
Потоки
Работа в процессе организована в виде потоков ( java thread ). Для отдела кадров, поток – это организация работы по обслуживанию группы. На самой первой картинке – один поток, последующих трёх – два. Внутри процесса потоки могут выполняться параллельно – два кадровика принимают две или более группы будущих сотрудников.
Взаимодействие кадровиков с группами – обработку потоков внутри процесса – называют синхронизацией потоков. На рисунках оформления одним кадровиком двух групп видны показаны способы: равномерный (девушка – юноша – девушка – юноша) и с разными приоритетами (две девушки чередуются с одним юношей).
Потоки имеют доступ к ресурсам процесса, к которому они относятся: группам к кадровику даны образцы бланков заявлений, ручки для заполнения документов. Но если потоки взаимодействуют с общими для них вещами – то возможны казусы. Если кадровик попросит крикнуть имя последнего человека в очереди – то, в случае с двумя группами, он не уверен заранее, что услышит женское имя или мужское. Подобные конфликты доступа к данным, блокировки и способы их разрешения – очень важная тема.
Состояния потока
- Создан ( New ) – очередь к кадровику готовится, люди организуются.
- Запущен ( Runnable ) – наша очередь выстроилась к кадровику и обрабатывается.
- Заблокирован ( Blocked ) – последний в очереди юноша пытается выкрикнуть имя, но услышав, что девушка в соседней группе начала делать это раньше него, замолчал.
- Завершён ( Terminated ) — вся очередь оформилась у кадровика и в ней нет необходимости.
- Ожидает( Waiting ) – одна очередь ждёт сигнала от другой.
Вернемся в IT-мир
В XXI веке многопоточное и параллельное выполнение стало актуальным. С 90-х годов прошлого века многозадачные операционные системы Windows, MacOS и Linux прочно обосновались на домашних компьютерах. В них часто можно встретить четырёх- и более ядерные процессоры. Число параллельных блоков GPU-видеокарт уже перевалило за тысячу.
Популярные программы пишутся с учетом многопоточности (multithreading), например, современные версии ПО обработки графики, видео или оперирующих большим объемом данных: Adobe Photoshop, WinRar, Mathematica, современные игры. Многопоточность Java – очень важная, востребованная и сложная тема. Поэтому в курсе JavaRush встречается много задач, чтобы разобраться с ней очень хорошо. Java-примеры на многопоточность помогут освоить основные нюансы и тонкости этой области, синхронизации работы потоков.
Процесс
Process (процесс) – выполняющийся экземпляр программы, которому Операционная Система (ОС) выделила память, процессорное время/ядра и прочие ресурсы. Важно, что память выделяется отдельно, адресные пространства различных процессов недоступны друг другу. Если процессам необходимо обмениваться данными, они могут это сделать с помощью файлов, каналов и иных способов межпроцессного взаимодействия.
Поток
Java Thread (поток). Иногда, чтобы не путать с другими классами Java – Stream и подобными, потоки Java часто переводят как нить. Они используют выделенные для процесса ресурсы и являются способом выполнения процесса. Главный поток выполняет метод main и завершается. При выполнении процесса могут порождаться дополнительные потоки (дочерние).
Потоки одного процесса могут между собой обмениваться данными. Многопоточность Java требует учитывать синхронизацию данных, не забывайте об этом. В Java процесс завершается тогда, когда закончил работу последний его поток. Для фоновых задач поток можно запустить как демон ( daemon ), отличие которого от обычного в том, что они будут принудительно завершены при окончании работы всех не- daemon потоков процесса.
Первое многопоточное приложение
Существует более полудюжины способов создания потоков, в рамках JavaRush курса мы их подробно разберём. Для начала познакомимся с одним из базовых. Имеется специальный класс Thread в методе run() которого необходимо написать код, реализующий логику программы. После создания потока, можно запустить его, вызвав метод start() . Напишем демонстрационную программу, реализующую пример многопоточности Java.
Запустим программу. В консоли виден вывод сообщения главным потоком. Далее, каждый дочерний поток queue1 и queue2 поочередно выводят сообщения в общую для них консоль об очередном обработанном сотруднике. Один из возможных вариантов работы программы:
Начали! Обработаны документы: Мария Обработаны документы: Иван Обработаны документы: Людмила Обработаны документы: Сергей Обработаны документы: Алиса Обработаны документы: Николай Обработаны документы: Карина Обработаны документы: Фердинанд Обработаны документы: Ольга Обработаны документы: Василий Process finished with exit code 0
Многопоточность в Java – тема трудная и многосторонняя. Умение писать код с использованием параллельных, многозадачных и многопоточных вычислений поможет вам эффективно реализовать задачи на современных многоядерных процессорах и кластерах, состоящих из множества компьютеров.
Источник: javarush.com