Потоки (threads) и многопоточное выполнение программ (multi-threading)
Презентацию к данной лекции Вы можете скачать здесь.
Введение
Многопоточность (multi-threading) – одна из наиболее интересных и актуальных тем в данном курсе и, по-видимому, в области ИТ вообще, и, кроме того, одна из излюбленных тем автора. Актуальность данной темы особенно велика, в связи с широким распространением многоядерных процессоров. В лекции рассмотрены следующие вопросы:
- Исторический обзор многопоточности
- Модели многопоточного исполнения
- Проблемы, связанные с потоками
- Потоки в POSIX (Pthreads)
- Потоки в Solaris 2
- Потоки в Windows 2000/XP
- Потоки в Linux
- Потоки в Java и .NET.
Однопоточные и многопоточные процессы
К сожалению, до сих пор мышление многих программистов при разработке программ остается чисто последовательным. Не учитываются широкие возможности параллелизма, в частности, многопоточности. Последовательный (однопоточный) процесс – это процесс, который имеет только один поток управления (control flow), характеризующийся изменением его счетчика команд. Поток (thread) – это запускаемый из некоторого процесса особого рода параллельный процесс, выполняемый в том же адресном пространстве, что и процесс-родитель. Схема организации однопоточного и многопоточного процессов изображена на рис. 10.1.
Как видно из схемы, однопоточный процесс использует, как обычно, код, данные в основной памяти и файлы, с которыми он работает. Процесс также использует определенные значения регистров и стек, на котором исполняются его процедуры. Многопоточный процесс организован несколько сложнее. Он имеет несколько параллельных потоков, для каждого из которых ОС создает свой стек и хранит свои собственные значения регистров. Потоки работают в общей основной памяти и используют то же адресное пространство, что и процесс-родитель, а также разделяют код процесса и файлы.
Многопоточность имеет большие преимущества:
- Увеличение скорости (по сравнению с использованием обычных процессов). Многопоточность основана на использовании облегченных процессов (lightweight processes),работающих в общем пространстве виртуальной памяти. Благодаря многопоточности, не возникает больше неэффективных ситуаций, типичных для классической системы UNIX, в которой каждая команда shell (даже команда вывода содержимого текущей директории ls исполнялась как отдельный процесс, причем в своем собственном адресном пространстве. В противоположность облегченным процессам, обычные процессы (имеющие собственное адресное пространство) часто называют тяжеловесными (heavyweight).
- Использование общих ресурсов. Потоки одного процесса используют общую память и файлы.
- Экономия. Благодаря многопоточности, достигается значительная экономия памяти, по причинам, объясненным выше. Также достигается и экономия времени, так как переключение контекста на облегченный процесс, для которого требуется только сменить стек и восстановить значения регистров, значительно быстрее, чем на обычный процесс (см. "Методы взаимодействия процессов" ).
Использование мультипроцессорных архитектур. Это особенно важно в настоящее время, в период широкого использования многоядерных гибридных и многопроцессорных систем. Именно многопоточность программ, основанная на многоядерности процессора, дает возможность, наконец, почувствовать реальные преимущества параллельного выполнения.
История многопоточности
Как небезынтересно отметить, один из первых шагов на пути к широкому использованию многопоточности, по-видимому, был сделан в 1970-е годы советскими разработчиками компьютерной аппаратуры и программистами. МВК "Эльбрус-1", разработанный в 1979 году, поддерживал в аппаратуре и операционной системе эффективную концепцию процесса, которая была близка к современному понятию облегченного процесса. В частности, процесс в "Эльбрусе" однозначно характеризовался своим стеком. Иначе говоря, все процессы были облегченными и исполнялись в общем пространстве виртуальной памяти – других процессов в "Эльбрусе" просто не было!
Концепция многопоточности начала складываться, по-видимому, с 1980-х гг. в системе UNIX и ее диалектах. Наиболее развита многопоточность была в диалекте UNIX фирмы AT&T, на основе которого, как уже отмечалось в общем историческом обзоре, была разработана система Solaris. Все это отразилось и в стандарте POSIX, в который вошла и многопоточность, наряду с другими базовыми возможностями UNIX.
Далее, в середине 1990-х гг. была выпущена ОС Windows NT, в которую была также включена многопоточность.
Однако в разных операционных системах API для многопоточности существенно отличались. Поэтому многопоточные программы, даже написанные на языках высокого уровня, оказались не переносимыми с одной платформы на другую, что, разумеется, создавало большие неудобства.
По-видимому, именно по причине различий в спецификациях и реализациях многопоточности в различных системах профессор Бьярн Страуструп не включил многопоточность в созданный им язык C++, ставший столь популярным, и его базовый набор библиотек. Программисты на языке C++ были вынуждены по-прежнему использовать многопоточность на уровне системных вызовов и библиотек конкретных операционных систем.
Важный шаг вперед сделали авторы языка Java и Java-технологии, первая версия реализации которых была выпущена в 1995 г. Именно в Java впервые многопоточность была реализована на уровне конструкций языка и базовых библиотек. В частности, в Java введен класс Thread,представляющий поток, и операции над ним в виде специальных методов и конструкций языка.
Платформа .NET, появившаяся в 2000 г., предложила свой механизм многопоточности, который фактически является развитием идей Java.
Различие подходов к многопоточности в разных ОС и на разных платформах разработки программ сохраняется и до настоящего времени, что приходится постоянно учитывать разработчикам. Для прикладных программ мы рекомендуем реализовывать многопоточность на платформе Java или .NET, что наиболее удобно и позволяет использовать высокоуровневые понятия и конструкции. Однако в нашем курсе, посвященном операционным системам, мы, естественно, больше внимания уделяем системным вопросам многопоточности и ее реализации в операционных системах.