Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Многопоточное параллельное программирование.pdf
Скачиваний:
123
Добавлен:
28.06.2014
Размер:
207 Кб
Скачать
☆

1. Многопоточное программирование: средства и реализация

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

Рис.1. Типичная архитектура построения параллельной программы.

Средства параллельного программирования позволяют распределить работу программы между двумя (или больше) процессорами в рамках одного физического или одного виртуального компьютера. Такую программу можно разбить на процессы (process) или потоки (thread). Типичная архитектура построения параллельной программы показана на рис. 1.

1.1 Понятие процесса и нити

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

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

Адресное пространство процесса делится на три логических раздела: текстовый (для кода программы), информационный (для данных программы) и стековый (для стеков программы). Логическая структура процесса показана на рис. 2. Текстовый раздел содержит подлежащие выполнению инструкции, которые называются программным кодом. Раздел данных содержит инициализированные глобальные, внешние и статические переменные процесса. Раздел стеков содержит локально создаваемые переменные и параметры, передаваемые функциям.

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

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

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

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

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

•Для переключения контекста требуется меньше системных ресурсов.

•Достигается более высокая производительность приложения.

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

•Программа имеет более простую структуру.

Простота доступности потоков к памяти процесса имеет свои недостатки:

•Потоки могут легко разрушить адресное пространство процесса.

•Потоки необходимо синхронизировать при параллельном доступе (для чтения или записи) к памяти.

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

Рис.2. Схема процесса, содержащего два потока.

Адресное пространство, выделенное для процесса, ограничивает размер стека, ограничивая тем самым размер, который может иметь каждый поток. На рис. 2 показана схема процесса, который содержит два потока. Как показано на рис. 2, процесс содержит два потока А и В, их стеки расположены в стековом разделе процесса. Потоки выполняют различные функции:

поток А выполняет функцию func1(), а поток В — функцию func2().

1.2 Многопоточное программирование.

1.2.1 Параллелизм данных (OpenMP)

Интерфейс OpenMP задуман как стандарт для программирования на масштабируемых SMPсистемах в модели общей памяти (shared memory model). В стандарт OpenMP входят спецификации набора директив компилятора, процедур и переменных среды. До появления OpenMP не было подходящего стандарта для эффективного программирования на SMPсистемах.

Наиболее гибким, переносимым и общепринятым интерфейсом параллельного программирования является MPI (интерфейс передачи сообщений). Однако модель передачи сообщений 1) недостаточно эффективна на SMP-системах; 2) относительно сложна в освоении, так как требует мышления в "невычислительных" терминах. POSIX-интерфейс для организации нитей (Pthreads) поддерживается широко (практически на всех UNIXсистемах), однако по многим причинам не подходит для практического параллельного программирования:

•слишком низкий уровень,

•нет поддержки параллелизма по данным.

OpenMP можно рассматривать как высокоуровневую надстройку над Pthreads (или аналогичными библиотеками нитей). Многие поставщики SMP-архитектур (Microsoft, Intel, Sun, HP, SGI) в своих компиляторах поддерживают спецдирективы для распараллеливания циклов. Однако эти наборы директив, как правило, 1) весьма ограничены; 2) несовместимы между собой; в результате чего разработчикам приходится распараллеливать приложение отдельно для каждой платформы. OpenMP является во многом обобщением и расширением упомянутых наборов директив.

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

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

Спецификация OpenMP для C/C++, содержит следующую функциональность:

•Директивы OpenMP начинаются с комбинации символов "#pragma omp". Директивы можно разделить на 3 категории: определение параллельной секции, разделение работы, синхронизация. Каждая директива может иметь несколько дополнительных.

•Компилятор с поддержкой OpenMP определяет макрос "_OPENMP", который может использоваться для условной компиляции отдельных блоков, характерных для параллельной версии программы.

•Распараллеливание применяется к for-циклам, для этого используется директива "#pragma omp for". В параллельных циклах запрещается использовать оператор break.

•Статические (static) переменные, определенные в параллельной области программы, являются общими (shared).

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

•Типы и функции OpenMP определены во включаемом файле <omp.h>.

•Кроме обычных, возможны также "вложенные" (nested) мьютексы - вместо логических переменных используются целые числа, и нить, уже захватившая мьютекс, при повторном захвате может увеличить это число.

Пример распараллеливания for-цикла в C

#pragma omp parallel for private(i)

#pragma omp shared(x, y, n)

for (i=0; i<n; i++)

{

a = a + x[i];

b = b + y[i];

}

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

1.2.2 Функциональный параллелизм (Posix Threads)

Поточно-ориентированная библиотека POSIX (Portable Operating System Interface — интерфейс переносимой операционной системы) содержит набор системных функций, которые в сочетании с языковыми средствами C++ успешно используются для поддержки параллелизма. Библиотека POSIX Threads является частью нового единого стандарта спецификаций UNIX (Single UNIX Specifications Standard) и включена в набор стандартов

IEEE, описывающих интерфейсы ОС для UNIX (IEEE Std. 1003.1-2001).

Соседние файлы в предмете Параллельные вычислительные системы