1 Введение в многопоточное программирование

advertisement
Министерство образования и науки Российской Федерации
САНКТ-ПЕТЕРБУРГСКИЙ ГОСУДАРСТВЕННЫЙ УНИВЕРСИТЕТ
ИНФОРМАЦИОННЫХ ТЕХНОЛОГИЙ, МЕХАНИКИ И ОПТИКИ
П.С. Алексеев
МНОГОПОТОЧНОЕ
ПРОГРАММИРОВАНИЕ
Учебное пособие
Санкт-Петербург
2010
Содержание
Содержание .............................................................................................................. 2
Введение ................................................................................................................... 5
1
2
3
4
5
6
Введение в многопоточное программирование............................................. 7
1.1
Классификация параллельных аппаратных архитектур по Флинну ..... 7
1.2
Понятия процессов и потоков. Основные определения ....................... 13
1.3
Переключение задач в многозадачных ОС. Многозадачность ............ 18
Аппаратная поддержка многозадачности .................................................... 21
2.1
Способы повышения быстродействия компьютеров ........................... 21
2.2
Параллельные компьютеры с общей памятью ...................................... 23
2.3
Вычислительные системы с распределённой памятью ........................ 26
2.4
GRID........................................................................................................... 27
2.5
Классификация параллельных компьютеров и систем ........................ 29
2.6
Производительность параллельных компьютеров ............................... 34
Основы многопоточных программ ............................................................... 50
3.1
Основные определения ............................................................................ 50
3.2
Виды ресурсов........................................................................................... 52
3.3
Типы взаимодействия процессов ............................................................ 53
3.4
Основные средства синхронизации ........................................................ 54
Реализация многопоточности на различных платформах .......................... 72
4.1
BSD, UNIX System V, POSIX, XPG ........................................................ 72
4.2
WinAPI ....................................................................................................... 74
4.3
.NET............................................................................................................ 76
4.4
Java ............................................................................................................. 78
Управление и синхронизация процессов и потоков ................................... 81
5.1
Функции создания процессов и управления ими .................................. 81
5.2
Функции создания потоков и управления ими ...................................... 83
5.3
Функции работы со средствами синхронизации ................................... 85
Системы программирования на основе передачи сообщений ................... 92
6.1
Основы MPI ............................................................................................... 92
2
7
6.1.1
Сообщения .......................................................................................... 93
6.1.2
Коммуникаторы .................................................................................. 94
6.1.3
Обмен сообщениями .......................................................................... 95
Новые средства многопоточного программирования ................................. 98
7.1
Совершенствование пулов потоков ...................................................... 104
7.2
Транзакционная память ......................................................................... 104
Приложения ......................................................................................................... 110
3
4
Введение
Целью дисциплины «Многопоточное программирование» является
сформировать у студентов компетенции, связанные с разработкой кода
программного продукта, с пониманием методологий построения
многопоточных приложений с использованием традиционных программных
и аппаратных средств распараллеливания.
Научить реализовывать многопоточность на различных платформах,
получить представление об управлении и синхронизации процессов и
потоков, получить практические навыки применения библиотеки MPI.
Способствование развитию таких личностных качеств, как аккуратность,
аналитическое мышление, ответственность и исполнительность, умение
планировать свою деятельность, осваивать новые методы в области
информационных систем.
Содержание дисциплины охватывает круг вопросов, связанных с
многопоточным программированием, разработкой параллельных
приложений, управлением и синхронизацией процессов и потоков,
применением различных библиотек в разработках. Дисциплина нацелена на
формирование общекультурных компетенций:
 способность развивать и совершенствовать свой интеллектуальный и
общекультурный уровень;
 способность к самостоятельному обучению новым методам
исследования в области информационных систем;
 способность проявлять инициативу, брать на себя ответственность;
 способность к профессиональной эксплуатации и применению
современных технологий.
К учебным задачам дисциплины относятся:
 Рассмотреть основные библиотеки, предназначенные для разработки
многопоточных программ: POSIX, WinAPI, .NET, Java, Parallel
Extentions.
 Научить студентов анализировать производительность параллельных
программ, а также получить навыки оптимизации.
 Сформировать практические и теоретические знания в области
многопоточного программирования, разработки многопоточных
приложений.
 Выработать навыки применения полученных теоретических знаний,
умение использовать современные технологии и методы в разработке
параллельных программ.
 Обучить терминологии предметной области.
 Готовность к практическому применению полученных знаний и
навыков.
5
 Научить оперировать базовыми понятиями, понимать принципы и
особенности создания многопоточных приложений, анализировать
требования.
 Дать представление об управлении и синхронизации процессов и
потоков.
 Дать теоретические сведения и практические навыки применения
библиотеки MPI.
 Научить обеспечивать согласованное функционирование и требуемый
уровень качества разрабатываемых приложений.
 Научить реализовывать многопоточность на различных платформах.
(BSD, UNIX System V, POSIX, UNIX X, WinAPI, .NET, Java).
 Ознакомить с тенденциями развития средств многопоточного
программирования.
Необходимыми условиями для освоения дисциплины «Многопоточное
программирование» являются: базовые знания программирования и
проектирования ПО, понимание предметной области, знание ООП, парадигм
ООП, знакомство с языком программирования C# и платформой Microsoft
.NET. Будет полезен опыт практической работы.
Для выполнения практических занятий необходима рабочая станция с
установленной платформой .NET, средой Visual Studio 2010.Все
необходимые используемые средства и библиотеки можно найти по
нижеуказанным ссылкам:
www.dreamspark.ru,
Microsoft Compute Cluster Pack SDK http://www.microsoft.com/downloads/details.aspx?familyid=D84623782F68-409D-9CB3-02312BC23BFD&displaylang=en
MPI.NET SDK http://www.osl.iu.edu/research/mpi.net/files/1.0.0/MPI.NET%20SDK.msi
6
1
Введение в многопоточное программирование
1.1 Классификация параллельных аппаратных архитектур по Флинну
Ниже приведена классификация аппаратных архитектур по Флинну.
 SISD, Single Instruction stream over a Single Data stream
o ОКОД – вычислительная система с одиночным потоком
команд и одиночным потоком данных. На рисунке 1
наглядно представлена схема архитектуры.
Рисунок 1. SISD архитектура
 SIMD, Single Instruction, Multiple Data
o ОКМД – вычислительная система с одиночным потоком
команд и множественным потоком данных. На рисунке 2
наглядно представлена схема архитектуры.
7
Рисунок 2. SIMD архитектура
1. Одна операция применяется сразу к нескольким элементам массива
данных
2. Параллельные операции выполняются на всех доступных вычислителях
3. Обработкой данных управляет единственная программа
4. Пространство имён является глобальным
5. Используются специализированные языки программирования и/или
библиотеки, предназначенные для конкретной архитектуры
 MISD, Multiple Instruction Single Data
o МКОД – вычислительная система со множественным
потоком команд и одиночным потоком данных. На рисунке
3 наглядно представлена схема архитектуры.
8
Рисунок 3. MISD архитектура
 MIMD, Multiple Instruction Multiple Data
o МКМД – вычислительная система со множественным
потоком команд и множественным потоком данных. На
рисунке 4 наглядно представлена схема архитектуры.
Рисунок 4. MIMD архитектура
9
1. Задача разбивается на несколько самостоятельных подзадач
2. Для каждой подзадачи разрабатывается отдельная программа
3. Программы подзадач выполняются на отдельных вычислителях
a. Для этого используются обычные языки программирования
4. Программы подзадач должны обмениваться информацией
a. Для этого используются функции специализированной
библиотеки
5. Преимущество
a. Теоретическая возможность достижения наивысшего
быстродействия, гибкость
 Дополнительная информация
http://en.wikipedia.org/wiki/Flynn%27s_taxonomy
1.1 Характеристики последовательных программ
Ниже приведены основные характеристики последовательных
программ.
 Неоптимальное использование аппаратных средств
o Невысокая производительность
 Использование традиционных (стандартных) средств
программирования
o Языков программирования
o Библиотек функций/классов
o Средств отладки и тестирования
 Инвариантность к возможностям параллелизма аппаратных
средств и платформ
o Относительно лёгкая переносимость
На рисунке 5 представлена схема последовательной программы.
10
Рисунок 5. Схема последовательной программы
1.2 Характеристики параллельных программ
Ниже приведены основные характеристики последовательных
программ.
 Оптимизация использования аппаратных средств
o Возможность преодолеть ограничение производительности
последовательных программ
 Использование средств программирования, не используемых при
разработке последовательных программ
o Языков программирования
o Библиотек функций/классов
o Средств отладки и тестирования
 Повышенная сложность проектирования и разработки
 Ориентация на возможности параллелизма конкретных
аппаратных средств и платформ
o Переносимость затруднена или невозможна
На рисунке 6 представлена схема параллельной программы.
11
Действие 1
Действие 4
Действие 2
Действие 5
Действие 3
Действие 6
Рисунок 6. Чистая параллельная программа
1.3 Особенности разработки параллельных программ
Основные особенности разработки параллельных программ.
 Управление параллельно выполняющимися действиями
 Обеспечение совместного использования общих ресурсов параллельно
выполняющимися действиями
 Необходимо выявлять и устранять ошибки, характерные только для
параллельных программ
o Взаимные блокировки
o Гонки
 Выявленные ошибки трудно повторить
o Часто проявляются архитектурные ошибки
o Не всегда возможно использовать отладчик
12
 Необходимо обеспечивать масштабируемость и балансировку загрузки
аппаратных средств
На рисунке 7 представлена схема реальной программы с элементами
параллелизма.
Действие 1
Действие 2
Действие 4
Действие 3
Действие 5
Действие 6
Рисунок 7. Реальная программа с элементами параллелизма
1.2 Понятия процессов и потоков. Основные определения
Поток – последовательность команд программы, выполняющихся одна
за другой в детерминированной последовательности.
Основной принцип потока – несколько потоков могут выполняться
параллельно (псевдопараллельно).
Поток (поток управления, задача, нить, thread) – одна из параллельно
(асинхронно) выполняющихся ветвей процесса
 Особенности потоков
o В процессе присутствует единственный главный поток
o Все потоки одного процесса работают в едином адресном
пространстве
13
 Общие переменные и код
 Нет необходимости использовать специальные средства
взаимодействия*
o Каждый поток имеет собственный стек
o Каждый поток имеет собственное состояние
 межпоточного взаимодействия обычно нужны
Процесс – выполнение пассивных инструкций компьютерной
программы на процессоре.
Процесс – совокупность взаимосвязанных и взаимодействующих
действий, преобразующих входные данные в выходные (ISO 9000:2000).
Процесс порождается при запуске программы.
Состав процесса:
Главный поток
Дополнительные потоки – необязательно
Память данных
Память программ
Все потоки в рамках одного процесса выполняются в едином адресном
пространстве, то есть используют общие переменные. Каждый поток имеет
собственный стек. Поток может порождать другие потоки.
Процесс (process) – совокупность действий процессора и необходимых
ресурсов для обеспечения выполнения инструкций программы
 Состав процесса
 Области памяти данных и программ
 Стек
 Отображение виртуальной памяти на физическую память
 Состояние
Типичные состояния процессов:
 Остановлен
o Процесс не использует процессор
 Терминирован
o Процесс закончился, но ещё не удалён
 Ожидает
o Процесс ждёт событие
 Готов
o Готов к выполнению, но ожидает освобождения процессора
 Выполняется
o Процесс выполняется процессором
На рисунке 8 представлен график состояний процесса.
14
1
1. Не
существует
3
2. Не
обслуживается
2
4
8
5. Ожидает
ресурс
4.
Выполняется
12
14
6 5
7
11
13
3. Готов
15
6. Ожидает
назначенное время
16
10
9
7. Ожидает
события
Рисунок 8. График состояний процесса
Межпроцессное взаимодействие – способ передачи информации
между процессами.
 Виды межпроцессного взаимодействия
o Разделяемая память
o Семафоры
o Сигналы
o Почтовые ящики
Событие – оповещение процесса со стороны ОС о возникновении
межпроцессного взаимодействия.
Примеры событий:
 Принятие семафором требуемого значения
 Поступление сигнала
 Поступление сообщения в почтовый ящик
Ресурс – объект (устройство, память), необходимый процессу или
потоку для выполнения заданных действий
Приоритет – целое число, определяющее важность каждого процесса
или потока в ОС:
 Чем больше приоритет у процесса или потока, тем больше
процессорного времени ему будет выделено
Виды ресурсов по природе:
 Аппаратные ресурсы
o Процессор
o Область памяти
o Периферийные устройства
15
o Прерывания
 Программные ресурсы
o Программа
o Данные
o Файлы
o Сообщения
Виды ресурсов по характеристикам:
 Активные
o Способны изменять и создавать информацию
(процессор)
 Пассивные
o Способны хранить информацию (память)
 Локальные
o Относятся к одному процессу
o После завершения процесса автоматически удаляются
 Разделяемые
o Относятся к нескольким процессам
o Удаляются только после окончания использования их
последним процессом
 Постоянные
o Для работы требуют операций «захватить» и
«освободить»
 Временные
o Для работы требуют операций «создать» и «удалить»
Виды разделяемых ресурсов:
 Некритичные
o Безопасно могут быть использованы одновременно
несколькими процессами и потоками
o Примеры: жёсткий диск в целом, сетевая карта,
видеокарта
 Критичные
o Безопасно могут быть использованы в один момент
времени только одним процессом или потоком
o Примеры: разделяемая память при её модификации
Типы взаимодействия процессов:
 Независимые процессы
o Процессы не используют разделяемые ресурсы
 Сотрудничающие процессы
o Процессы разделяют канал коммуникации: один пишет,
другой читает
16
o Процессы работают по очереди: один работает, второй
ожидает завершения работы первого
 Конкурирующие процессы
o Процессы используют совместно разделяемый ресурс
o Процессы используют критические секции
o Процессы используют взаимные исключения
(мьютексы)
Критическая секция – участок программного кода, который
допускается выполнять только единственным потоком (процессом)
Взаимное исключение (мьютекс, mutual exclusion, mutex) – способ
синхронизации потоков за счёт использования захвата совместно
используемого ресурса, также называемого мьютексом
 Если мьютекс занят, то при попытке его захвата поток переходит
в состояние ожидания
 Как только мьютекс освобождается, ранее ожидавший поток
высвобождается, а мьютекс вновь считается захваченным
Безопасное взаимодействие – целостность информации и неделимость
действий при взаимодействии обеспечиваются операционной системой
Небезопасное взаимодействие – целостность информации и
неделимость действий при взаимодействии обеспечиваются приложением
Разделяемая память – область памяти, одновременно доступная для
нескольких процессов.
 Это базовый вид взаимодействия процессов, к которому сводятся
все остальные виды взаимодействия
 Разделяемая память может отображаться на разные области
виртуальной памяти, поэтому нужно преобразовывать указатели
Операции:
 Создать – создаётся объект (файл), недоступный для
использования
 Подсоединить – созданный объект разделяемой памяти
присоединяется к адресному пространству процесса, после этого
разделяемой памятью можно пользоваться для обмена данными
 Отсоединить – объект разделяемой памяти отсоединяется от
адресного пространства процесса
 Удалить – процесс сообщает ОС о том, что больше не будет
использовать разделяемую память, реально объект разделяемой
памяти будет удалён после окончания его использования
последним процессом
17
Семафоры - это объект синхронизации, задающий количество процессов
и/или потоков, имеющих одновременный доступ к разделяемому ресурсу
 По сути это безопасный счётчик
 Значение счётчика может быть меньше 0 – это значит, что
несколько потоков/процессов ожидают освобождения
семафора
 Виды
o Двоичные (булевские)
o Счётные
Операции с семафорами:
 Взять (Get) – уменьшает значение счётчика на k, если в счётчике
значение не меньше k, иначе поток блокируется
 Вернуть (Put) – увеличивает значение счётчика на k
 Попробовать взять (TryGet) – то же, что и Get, но не блокирует поток
 Проверить (Test) – возвращает значение счётчика
 Блокировать (Lock) – «обнулить», если значение счётчика больше 0
 Разблокировать (Unlock) – вернуть столько, сколько сняли при
блокировке.
Mutex (mutual exclusion) – механизм синхронизации, предназначенный для
устранения недостатков семафоров:
o Захват семафора после его случайного разблокирования всегда
удачен и может привести к повреждению данных
o Семафор является объектом ОС, поэтому для доступа к нему
необходимо переключать задачи
 Состав мьютекса
o Булевский семафор
o Идентификатор потока, захватившего разделяемый ресурс
 Мьютексы хранятся в памяти процесса (локальные) или в разделяемой
памяти процессов (глобальные)
 Операции
o Захватить (Lock) – если мьютекс захвачен другим потоком,
текущий поток блокируется
o Освободить (Unlock) – работает только для потока, являющегося
владельцем мьютекса
o Попробовать захватить (TryLock) – то же, что и Lock, но
текущий поток не блокируется
1.3 Переключение задач в многозадачных ОС. Многозадачность
18
Многозадачность (multitasking) – свойство ОС или среды исполнения
обеспечивать возможность параллельной (или псевдопараллельной)
обработки нескольких процессов.
Настоящая многозадачность возможна только в системах с несколькими
процессорами (ядрами).
Типы многозадачности:
 Процессная – планировщик ОС может управлять
процессами
 Поточная – планировщик ОС может управлять процессами
и потоками
Многозадачность называется невытесняющей, когда
 Загружаются в память два или более приложений
 Процессорное время предоставляется только основному приложению
 Второе приложение становится фоновым
 Для выполнения фонового приложения его необходимо активизировать
Многозадачность называется кооперативной (совместная), когда
 Для выполнения следующей программы текущая программа должна
объявить о готовности отдать процессор
Преимущества такого вида многозадачности:
o Не нужно защищать совместно используемые ресурсы
o Легко преобразовать обычные программы в параллельные
Недостаток такого вида:
o Выполняющаяся задача может не отдать процессор и остальные
задачи не смогут выполниться
o Трудно организовать многопоточную архитектуру ввода-вывода
в ядре ОС в случае:
 Одна задача инициировала операцию ввода-вывода и ждёт
её завершения
 В это время следует переключиться на другую задачу
Многозадачность называется вытесняющей, когда
 Для выполнения следующей программы ОС принудительно
приостанавливает выполнение текущей программы
 Планировщик задач
 Эффективное использование приоритетов
 Преимущества:
o Повышение надёжности
o Возможность реализовать многопоточный ввод-вывод в ядре ОС
o Возможность использования многоядерных и
многопроцессорных систем
 Недостатки:
o Усложняется разработка приложений
o Возникает требование реентерабельности
o Необходимость защиты совместно используемых ресурсов
19
В приложении 1 можно ознакомиться с примерами параллельных программ.
20
2
Аппаратная поддержка многозадачности
2.1 Способы повышения быстродействия компьютеров
Архитектуры процессоров CISC и RISC
CISC – Complete Instruction Set Computer
o Основная идея – сокращение количества машинных циклов на
выполнение одной инструкции
o Увеличивалось количество инструкций
o Использовался микрокод
RISC – Reduced Instruction Set Computer
o Сложные инструкции CISC используются редко – зачем их
поддерживать, пусть все инструкции будут простыми
o Вместо микрокода в ПЗУ (медленно) используется программа в
ОЗУ (быстро)
o Необходимо использование оптимизирующих компиляторов,
зато не нужно писать на ассемблере
o Все инструкции имеют одинаковую длину и состоят из битовых
полей, которые можно декодировать одновременно
o Используется большое количество регистров для сокращения
количества обращений к памяти
Параллельная обработка
 Использование двух и более независимых устройств для обработки
данных
 Эффективно для выполнения одной и той же операции для
элементов массивов
Конвейризация
Типовые стадии конвейера – Выборка, Декодирование, Исполнение,
Запись результатов. Конвейризация хорошо работает для обработки
несвязанных данных. Плохо работает с ветвлениями и взаимозависимыми
данными, а также в CISC. Однако, проблемы стараются решить с помощью
таблицы регистров и переименования регистров.
21
Конвейризация стадий конвейера – суперковейерные компьютеры,
однако это требует значительного увеличения тактовой частоты. Выполнение
более одной операции одновременно – суперскалярные компьютеры (Два и
более конвейеров, предусмотрен специальный аппаратный модуль,
направляющий инструкции на разные конвейеры).
Производительность конвейерного устройства вычисляется по
следующей формуле:
n – количество элементов в векторе
σ – время на инициализацию векторной команды
l – количество ступеней конвейера
τ – длительность выполнения ступени конвейера.
На рисунке 8 изображена зависимость производительности
конвейерного устройства от длины входных данных.
Рисунок 9. Зависимость производительности конвейерного устройства
от длины входных данных.
Кэш-память
Кэш-память - это быстрое ОЗУ, расположенное между основной
памятью и процессором
 Основные действия
o Кэш-промах
o Кэш-попадание
 Виды кэша
22
o Единый кэш – инструкции и данные лежат в одной памяти
o Гарвадская архитектура – данные и инструкции разделены
 Уровни кэша
o L1 – на частоте процессора
o L2 – на частоте процессора или её половине
o L3 – на частоте внешней шины
4.1 Законы Амдала
Первый закон
 Производительность вычислительной системы, состоящей из
связанных между собой устройств, в общем случае определяется
самым непроизводительным элементом
Второй закон
 Система состоит из s простых одинаковых универсальных
устройств
 При выполнении параллельной части алгоритма все устройства
загружены одинаково
 β – доля последовательных вычислений
 Максимально возможное ускорение:
Третий закон
 Система состоит из простых одинаковых универсальных
устройств
 При любом режиме её работы ускорение не может превзойти
величины, обратной доли последовательных вычислений
2.2 Параллельные компьютеры с общей памятью
Основные термины:
SMP – symmetrical multiprocessor system, Сильно связанные процессоры.
VLIW – Very Large Instruction Word.
NUMA – Non Uniform Memory Access – физически память разделена,
логически – едина.
На рисунке 10 представлена схема общей архитектуры
23
Рисунок 10. Схема общей архитектуры
На рисунке 11 представлена схема использования общей шины.
Рисунок 11. Использование общей шины
На рисунке 12 представлена схема использования матричного
коммутатора.
Рисунок 12. Использование матричного коммутатора
24
На рисунке 13 представлена схема Омега-сети
Рисунок 13. Омега-меть
На рисунке 14 представлены виды связей процессоров.
Рисунок 14. Виды связи процессоров
25
2.3 Вычислительные системы с распределённой памятью
На рисунке 14 представлена схема общей архитектуры
Рисунок 15. Общая архитектура
На рисунке 16 представлена структура типового кластера
Рисунок 16. Структура типового кластера
26
2.4 GRID
Грид вычисления — это форма распределенных вычислений, в которой
«виртуальный суперкомпьютер» представлен в виде кластера соединённых с
помощью сети, слабосвязанных компьютеров, работающих вместе для
выполнения огромного количества заданий (операций, работ). Эта
технология была применена для решения научных, математических задач,
требующих для решения значительных вычислительных ресурсов. Грид
вычисления используются также и в коммерческой инфраструктуре для
решения таких трудоёмких задач как экономическое прогнозирование,
сейсмоанализ, разработка и изучение свойств новых лекарств. На рисунке 17
представлена структура GRID.
Распределенные или Grid вычисления вцелом являются
разновидностью параллельных вычислений, которое основывается на
обычных компьютерах (со стандартными процессорами, устройствами
хранения данных, блоками питания и т. д.) подключенных к сети (локальной
или глобальной) при помощи обычных протоколов, например Ethernet. В
отличии от суперкомпьютеров, которые содержат множество процессоров,
подключенных к локальной высокоскоростной шине.
Основным преимуществом распределённых вычислений является то,
что отдельная ячейка вычислительной системы может быть приобретена как
обычный неспециализированный компьютер. Таким образом можно
получить практически те же вычислительные мощности, что и на
обычных супер компьютерах, но с гораздо меньшей стоимостью.
В настоящее время выделяют три основных типа GRID-систем:
1. GRID на основе использования добровольно предоставляемого
свободного ресурса персональных компьютеров (добровольная GRID);
2. Научная GRID — хорошо распараллеливаемые приложения
программируются специальным образом (например, с использованием
Globus Toolkit);
3. GRID на основе выделения вычислительных ресурсов по
требованию (Enterprise GRID или коммерческая GRID) — обычные
коммерческие приложения работают на виртуальном компьютере,
который, в свою очередь, состоит из нескольких физических
компьютеров, объединённых с помощью GRID-технологий.
Система имеет в основе клиент-серверную архитектуру с, так
называемым, "толстым" клиентом - то есть, клиентская часть берет на себя
все необходимые данные для расчетов у сервера и затем обращается к нему
только с определенным результатом. Задача сервера: корректно обработать
запросы клиентов и синхронизировать имеющиеся данные между ними, при
этом правильно выводя результаты администратору сети.
27
Рисунок 17. Структура GRID
Рассмотрим обобщенную структуру GRID сетей на примере системы
обрабатывающей данные с Большого Большого адронного коллайдера CERN
(см. рисунок 18)
 Верхний уровень Tier 0 — получение информации с детекторов, сбор
«сырых» научных данных, которые будут храниться до конца работы
эксперимента
 Tier 1 — хранение второй копии этих данных в других уголках мира
(11 центров: в Италии, Франции, Великобритании, США, на Тайване, а
один центр первого уровня — CMS Tier1 — в ЦЕРНе). Центры
обладают значительными ресурсами для хранения данных.
 Tier 2 — следующие в иерархии, многочисленные центры второго
уровня. Наличие крупных ресурсов для хранения данных не
обязательно; обладают хорошими вычислительными ресурсами.
Российские центры: в Дубне (ОИЯИ, Объединенный институт ядерных
исследований), три центра в Москве (НИИЯФ МГУ, ФИАН, ИТЭФ —
Институт теоретической и экспериментальной физики), Троицке (ИЯИ,
Институт ядерных исследований), Протвино (ИФВЭ, Институт физики
высоких энергий) и Гатчине (ПИЯФ). Кроме того, в единую сеть с
этими центрами связаны и центры других стран-участниц ОИЯИ — в
Харькове, Минске, Ереване, Софии и Тбилиси.
Более 85 % всех вычислительных задач БАК сейчас выполняется вне
ЦЕРНа, из них более 50 % на центрах второго уровня.
 Tier 3 — Институты-участники программы
28

Tier 4 — Рабочие станции пользователей, позволяющих использовать
их для нужд проекта
Рисунок 18. Структура CERN GRID
2.5 Классификация параллельных компьютеров и систем
Ранее была рассмотрена классификация Флинна. Её основные
недостатки:
 Векторно-конвейерные машины не достаточно хорошо вписываются в
эту классификацию
 Слишком много архитектур, относящихся к классу MIMD
Термин "Grid-вычисления" (Computing grid), где "grid" означает "решетка,
сетка, сеть", по смыслу аналогичен выражению "единая энергосистема". Суть
его заключается в стремлении объединить все компьютеры мира в единую
систему — в виртуальный суперкомпьютер невиданной мощности, что
позволит распределять и перераспределять ресурсы между пользователями в
соответствии с их запросами. Именно так человечество пользуется
электричеством единых энергетических сетей. Основу подобного
объединения можно рассматривать и для транспортных сетей, сетей
обеспечения водой, нефтью, газом и т.д.
Имея такой суперкомпьютер, пользователь может в любое время и в любом
месте запросить столько вычислительных ресурсов, сколько ему требуется
для решения своей задачи. Более того, он может заказать ее решение, послав
запрос-заявку на сайт одного из центров обслуживания. Так же, как единая
29
энергосистема локально испытывает переменную нагрузку (потребление
электроэнергии в отдельной стране, регионе или населенном пункте
изменяется в зависимости от времени суток, года и т.д.), так и система Gridвычислений способна перераспределять мощности, направляя запросы на
недогруженные компьютеры в соответствии с возросшими требованиями.
Предоставление вычислительных ресурсов по требованию связывают с
известным понятием "коммунальная услуга", что вполне соответствует
тенденции объединения и распределения ресурсов жизнеобеспечения.
Однако на Grid-технологии возложены задачи и информационного характера:
сделать всю информацию мира оперативно доступной. Это означает, что
пользователь должен ощущать, что вся информация, за которой он
обратился, находится здесь, рядом, не далее чем на сервере того провайдера,
к которому он подключен.
И мы ловим себя на мысли, что говорим об Интернете!
Да, в сущности, речь идет о смелом, фантастическом развитии именно того,
что фантастическим казалось вчера, о развитии всемирной паутины, о
внедрении в нее вычислительных функций и о повышении качества
информационного обслуживания. Именно так и намечается дальнейший путь
развития Интернета: от WWW к GRID1).
Таким образом, для Grid-технологии характерны два (традиционно
смешиваемых) направления развития, определяемые их важностью и
различиями в исследовании.
Первое направление, связанное с информационными задачами, представляет
сегодня сущность Интернета. Это — обмен новостями, приобретение новых
знаний, программных средств, мульти-медиа-развлечения, диалоговые
системы телеконференции, электронная почта, финансовые операции и т.д.
Эти функции "Всемирной Паутины" уже представляются достаточными
самым широким слоям населения, покрывая весь компьютерный сервис. Они
органично вошли в базовое воспитание детей, определяя уровень
грамотности современного человека.
30
Однако именно здесь существует основная проблема. Неуправляемое
стихийное, даже "дикое" развитие Интернета приводит к парадоксу:
доступность огромных объемов мировой информации породила
значительный вес отказов, ответов о действительной недоступности этой
информации, о превышении допустимого времени поиска, об ошибках.
Поисковые системы блуждают по иерархической, запутанной системе Webсерверов, натыкаясь на их перегрузку, образуя трафик для обратного
движения информации. Мы с отчаянием следим за замершей планкой
индикатора, в ожидании выдачи такой знакомой записи "Error..." — с
указанием предположения об окончании допустимого времени адресного
обращения.
Сделать всю информацию доступной, установить гарантированное время
поиска, породить у пользователя ощущение того, что вся информация по его
запросу находится рядом, — это фантастическая задача. Можно ли
объединить всю информацию мира, блуждающую в Интернете, в одну
огромную базу данных, размеры которой даже трудно оценить? Да еще с
неограниченным многоканальным доступом?
Движение в направлении осуществления этой глобальной идеи и является
задачей исследователей в области Grid-технологий. Ясно, что идеального
решения эта задача может никогда не получить. Однако стремиться к
минимизации времени выполнения запроса, несмотря на структурную
"удаленность" информации, несомненно, следует.
Второе направление развития Grid-технологий связано, как ранее говорилось,
с таким расширением функций Интернета, при котором пользователь может
получать ответы на запросы вычислительного характера. Речь, конечно, идет
о тех задачах, решение которых недоступно широкому пользователю —
ученому, управленцу, коллективу, всем, кому необходима достоверная
информация для важных решений. Интернет должен принимать заказ на
работу с задачами высокой сложности, большой размерности, с задачами
моделирования сложных физических явлений, таких, например, как точный
метеорологический прогноз, с оптимизационными задачами хозяйственного
планирования, с задачами статистической обработки экспериментов, с
задачами — запросами по контролю космического пространства, с
имитационными задачами для испытания новых технических средств и т.д.
31
Это означает, что в составе Интернета должны быть мощные
вычислительные центры, снабженные развиваемыми пакетами прикладных
программ решения сложных задач, оболочками для решения классов задач.
Такие центры должны комплектоваться высококвалифицированными
математиками, развивающими вычислительную базу, готовыми принимать
заказы, консультировать пользователей при "доводке" заказов до требуемого
формального представления, ибо мечты о полной автоматизации процесса их
выполнения еще долго будут неисполнимыми.
Необходимо учесть и селекцию допустимых заказов: при желании, например,
отстранить школьника-разгильдяя, препоручающего выполнение урока
домашнему компьютеру.
При построении Grid-систем особую актуальность обретают проблемы
защиты. Цель защиты — перекрыть доступ к ресурсам и информации для тех
пользователей, которые не имеют соответствующих привилегий. Таким
пользователем может стать и собственник, предоставивший свой компьютер
в аренду или по договору на время простоя.
Базовыми элементами защиты являются:
•аутентификация — проверка подлинности участника взаимодействия с
системой;
•механизм авторизации — проверка допустимости затребованной операции;
•конфиденциальность и целостность данных — защита от нелегитимного
доступа;
•биллинг и аудит — способность контролировать и подсчитывать объем
использованных ресурсов и обеспечиваемых ими служб;
•строгое выполнение обязательств — способность устанавливать, что данный
участник выполнил определенную задачу или согласился на ее выполнение,
даже если сам он это отрицает.
Классификация Фенга (1972)
32
Две характеристики
 n – количество бит в машинном слове, обрабатываемых параллельно
(обычно совпадает с длиной машинного слова)
 m – количество слов, обрабатываемых одновременно вычислительной
системой (ширина битового слоя)
Максимальная степень параллелизма (пиковая производительность)
P=nxm
Преимущество: введение единой числовой метрики.
Недостатки:
 Нет различия между процессорными матрицами, векторноконвейерными и многопроцессорными системами
 Нет учитывается способ получения возможности обработки более
одного слова, например, за счёт конвейера или за счёт независимых
процессоров
Классы систем по Фенгу:




Разрядно-последовательные, пословно-последовательные (n = m = 1)
Разрядно-параллельные, пословно-последовательные (n > 1, m = 1)
Разрядно-последовательные, пословно-параллельные (n = 1, m > 1)
Разрядно-параллельные, пословно-параллельные (n > 1, m > 1)
Классификация Хендлера
Данная классификация была введена для устранения недостаток
предыдущих рассмотренных классификаций. Рассмотрим её основные
особенности.
В данной классификации для параллельных системы вводится функция
вида:
T(C) = (k x k’, d x d’, w x w’),
где
k – Число процессоров, работающих параллельно
k' – Глубина макроконвейера из отдельных процессоров
d – Число АЛУ в каждом процессоре, работающих параллельно
d' – Глубина конвейера из функциональных устройств АЛУ
w – Число разрядов в слове, обрабатываемых АЛУ одновременно
33
w' – Число ступеней в конвейере функциональных устройств АЛУ
x – отражает конвейерный принцип обработки (прохождение данных сначала
через одну подсистему, затем через другую)
+ – отражает возможность независимого использования процессоров
разными задачами
v – операция альтернативы, показывающая возможные альтернативные
режимы работы системы
Для каждой системы составляется такая формула, дальнейшее
сравнение систем производится посредством эти х формул.
2.6 Производительность параллельных компьютеров
Существуют различные способы оценки производительности
параллельных вычислительных систем за счёт введения различных числовых
метрик, а также их сравнения по производительности.
Пиковая производительность
Иное название данного вида оценки производительности –
теоретическая производительность. Данная метрика определяется как число
команд, выполняемых компьютером в единицу времени.
Единица измерения пиковой производительности, определяемой таким
образом: MIPS – Million Instructions per Second. Эта единица измерения
получила широкое распространение.
Альтернативный способ определения производительности –
вычисление максимального количества вещественных операций,
выполняемых в единицу времени. В этом случае применяется другая
известная единица измерения FLOP – Floating Point Operations per Second.
Основным преимуществом оценки производительности параллельных
компьютеров является простота выполнения расчёта производительности.
Самый главный недостаток – пиковая производительность практически
никогда не достигается на практике, поэтому пиковая производительность не
позволяет оценить скорость работы вычислительной системы при решении
реальных задач.
Для устранения недостатка пиковой производительности используются
различные синтетические тесты, называемые бенчмарками. Приведём
34
примеры наиболее известных бенчмарков, применяемых для оценки
производительности параллельных вычислительных систем:
 LINPACK – решение системы уравнений.
 STREAM – четыре небольших цикла, работающих с очень
длинными векторами.
Если несколько процессоров составляют ВС, то важной характеристикой ее
эффективности эффективности (основные составляющие эффективности —
производительность, надежность, стоимость) при специализированном
использовании (например, в составе АСУ) является коэффициент загрузки
процессоров kЗ. Для его определения находят коэффициенты загрузки
процессоров
Если P0 — производительность одного процессора, то реальная
производительность ВС, состоящей из n процессоров, при решении данной
задачи (!) составляет
PBC = n kЗ P0.
P0 определяется классом решаемых задач.
Идеальным способом его определения является использование самих задач.
Однако при предварительной оценке возможностей ВС может еще не
существовать алгоритмов той системы, в которой предполагается
использовать ВС. Либо класс решаемых задач может быть достаточно широк.
Известны несколько подходов к формированию тестов, по которым
определяется производительность P0 единичных ЭВМ или процессоров ВС.
1.Смеси операций различных типов в случайном порядке, отражающие их
процентное соотношение в задачах интересующего класса.
35
Для вычислительных задач применялась (утвержденная ГОСТом) смесь
"Гибсон-3". Она хорошо отражала архитектуру ЕС ЭВМ, воспроизводящей
архитектуру IBM. Однако ранее говорилось о тенденции повышения уровня
языка пользователя, об аппаратной поддержке ЯВУ. Смесь Гибсона,
приведенная ниже, не отражает этих тенденций. Набор операций
примитивен, соответствует ЭВМ ранних поколений. Интерпретация в ней
"языковых" операций затруднительна и уменьшает точность оценки. Ее
использование определялось требованиями советских ГОСТов.
Смесь "Гибсон-3" 1. Загрузка регистра без индексации 31 %
2. Загрузка регистра с индексацией 18 %
3. Проверка условия и переход 17 %
4. Сравнение 4 %
5. Сдвиг на 3 разряда 4 %
6. Логическая операция "И" 2 %
7. Команды с минимальным временем выполнения 5 %
8. Сложение с фиксированной запятой 6 %
9. Умножение с фиксированной запятой 0,6 %
10. Деление с фиксированной запятой 0,2 %
11. Сложение с плавающей запятой 7 %
12. Умножение с плавающей запятой 4 %
13. Деление с плавающей запятой 1,5 %
Приведенная смесь Гибсона интересна статистически произведенными
оценками состава операций в решаемых задачах вычислительного
характера. Это полезно знать и в других случаях выбора и оценки
архитектурных решений.
36
Известен принцип построения тестов на основе смеси по методу Ветстоуна,
где в состав операций входят операции, реализующие сложные языковые
конструкции.
2.Ядра. Ядро — небольшая программа, часть решаемой задачи.
Характеристики ядра могут быть точно измерены. Известны ядра Ауэрбаха:
коррекция последовательного файла и файла на диске, сортировка,
обращение матрицы и др.
3.Бенчмарки — реальные программы, характеристики которых можно
оценить или измерить при использовании. Обычно берут из числа тех, для
которых разрабатывается система.
4.Программа синтетической нагрузки — параметрически настраиваемая
программа, представляющая смеси определенных программных
конструкций. Позволяет воссоздать набор вычислительных характеристик,
свойственных большинству программ, для решения которых используется
или разрабатывается ВС.
5.У нас в стране в аналогичном применении больше известна модель
вычислительной нагрузки.
Она применяется на уровне решения вопроса: какие вычислительные
средства поставить в систему или заказать их разработку?
Модель позволяет перейти с уровня оценки одного процессора ВС на
уровень комплексной оценки ВС. Составляется и параметризуется с учетом
сложной структуры ВС и ее устройств, параллельного участия этих устройств в
решении задач, доли участия и порядка взаимодействия устройств.
37
Например, на основе статистических оценок решаемых в системе управления
частных задач (функциональных модулей) строится обобщенная модель
программы, в которой возможна следующая параметрическая настройка:
•среднее количество непрерываемых операций, выполняемых процессором;
•частота обращений к ОП, связанных с запуском механизмов виртуальной
памяти, возможностью конфликтов в связи с расслоением памяти,
мультипроцессорной обработкой;
•частота обращений к ОС (для организации выполнения процедур на стеке,
индексации массивов, обращения к очереди "к процессору",
мультипрограммного режима, синхронизации в режиме реального времени,
отработки прерываний, счета стандартных функций и др.) и связанные с этим
потери полезной производительности;
•сценарий параллельного ввода-вывода информации с учетом ее объема;
•частота или сценарий внутрисистемного обмена, связанного с
необходимостью синхронизации работ в совокупности с параллельным
выполнением;
•количество процессоров и других функциональных устройств в системе.
Однако все эти тесты не дают ответ на традиционный вопрос: какое
количество операций в секунду выполняет ВС? Все усложняющаяся структура
и расширяющееся множество "нетрадиционных" операций затрудняют
ответ.
Тогда особую важность обретают сравнительные оценки исследуемой ВС с
другими ЭВМ или ВС, для которых уже известны значения
производительности по числу операций в секунду.
38
Итак, структурные средства повышения уровня языка, параллелизм работы
устройств, введение дополнительных операций все с меньшей
определенностью соответствуют традиционным тестам.
Пример — проблема оценки ЦП с многофункциональным АЛУ: как их
загружать для реальной оценки (сколько их реально будут загружено в
каждом такте), какой поток обмена с ОП предполагать и т.д.?
Используется сравнительная оценка характеристик решения задачи на
исследуемой ВС и на ВС или ЭВМ, для которой уже известны характеристики
производительности.
Одна и та же задача решается на известной ЭВМ, для которой (из-за ее более
простой организации) известно значение производительности или
быстродействия; например, ЕС 1066 имеет производительность 5,5 млн
оп./с. Однопроцессорный МВК "Эльбрус-2" контрольную физическую задачу
решает за 6,5 часа, ЕС 1066 — за 21 час. Значит, эквивалентная
производительность "Эльбрус-2" в комплектации с единственным
процессором составляет ∼ 5,5 × 21/6,5 = 17,6 млн оп./с (в сравнении с ЕС
1066). Аналогично проводилось сравнение с ЭВМ БЭСМ-6.
На этапе испытаний ВС при оценке производительности складывается
больше определенности по будущему режиму ее эксплуатации. Есть
возможность построения контрольной задачи (КЗ), (развивающей идеи
бенчмарок), использующую всю ВС. КЗ строится на основе типовых задач
аналогичных систем управления, использует развиваемый прототип
системы, воссоздает реальный режим решения. Для КЗ оценивается
количество так называемых алгоритмических операций (аналог того, что
отражено в смеси Ветстоуна). Т.е. можно максимально приблизить
вычислительную нагрузку ВС к реальной ожидаемой. Это — современный
комплексный подход, когда КЗ, составленная для всей ВС как для единой
установки, учитывает не только использование процессоров, но и затраты на
39
организацию вычислительного процесса, издержки ОС, интенсивность
параллельного обмена информацией, организацию помехозащищенного
вычислительного процесса.
Относительно последнего следует подчеркнуть: строятся контрольные
задачи, по которым производительность оценивается с учетом надежности.
Поскольку мы не занимаемся разработкой и эксплуатацией ВС как комплекса
радиоэлектронной аппаратуры, рассмотрим проблемы оценки и повышения
надежности ВС как средства решения конкретных специальных задач.
В этом смысле под надежностью ВС будем понимать вероятность решения
поставленной перед ней задачи.
Тогда надежность ВС в составе сложной системы управления определяется
следующими факторами:
•вероятностью пребывания в исправном состоянии в момент начала цикла
управления;
•вероятностью пребывания в исправном состоянии в течение всего цикла
управления;
•помехоустойчивостью, т.е. способностью с допустимыми потерями
временных, точностных и аппаратурных ресурсов на требуемом
качественном уровне завершить цикл управления при возникновении
неисправностей.
Построим дерево логических возможностей (рис. 12.2) для нахождения всех
вероятностных составляющих вычислительного процесса в ВС.
(Дерево логических возможностей строится следующим образом. При
исходе из одной вершины на каждом уровне ветвления вводится
40
исчерпывающее множество событий, т.е. сумма их вероятностей равна
единице. Вероятности событий проставляются на дугах. Тогда вероятность
интересующей нас совокупности событий находится как сумма
произведений вероятностей, отмечающих пути, которые ведут к данным
событиям. Пример проиллюстрирует сказанное выше.)
1.Одной из определяющих характеристик надежности является коэффициент
готовности К Г
где Tвосст — среднее время восстановления (в т.ч. ремонта) после отказа.
Т.е. к началу цикла управления с вероятностью КГ ВС приступит к решению
своей задачи.
2.Если ВС приступила к решению задачи, то возможны три варианта:
◦в течение всего времени t решения задачи (или t — цикл управления) ВС
работала безотказно. Т.е. без каких-либо осложнений задача решена в
предположении, что программы составлены правильно;
◦произошел сбой (не усложняя проблемы, учитывая обычную
быстротечность процессов, считаем, что сбой — единственный);
◦произошел отказ.
Примечание. Сбой — самоустранимый отказ. В результате случайных
наводок в некоторой цепи может появиться или пропасть сигнал. Сбои
приводят (по классификации разработчиков МВК "Эльбрус") к синхронным и
асинхронным авариям. При синхронной аварии (ее вызывают
преимущественно сбои в ЦП) может быть установлена и повторена команда,
при исполнении которой авария произошла. При многочисленных передачах
информации внутри ВС используются коды, исправляющие ошибки. Значит, в
этом случае сбои также приводят к синхронным авариям.
41
При асинхронной аварии вычислительный процесс нарушается необратимо.
При должном уровне аппаратного контроля современных ВС синхронные
аварии считаются "невидимыми" пользователю — команды перезапускаются
аппаратным контролем.
Примечание. Приведенные ниже выкладки хорошо известны и бесспорны
для отдельных ЭВМ (однопроцессорных ВС). Поэтому в последующих
разделах будет показано, как они обобщаются и переносятся на общий
случай ВС.
Напомним модель надежности.
Пусть λ1 — частота сбоев (количество сбоев в единицу времени), найденная
как одна из характеристик данной ЭВМ; λ2 — частота отказов; λ1 + λ2 = λ.
Тогда λ t — количество сбоев и отказов за интересующее нас время t— цикл
управления.
Разобьем отрезок t на множество n элементарных отрезков. Можно считать
вероятность сбоя или отказа на таком элементарном отрезке равной λt/ n.
Вероятность бессбойной и безотказной работы на элементарном отрезке
равна 1 − λt/ n. Вероятность того, что на всех элементарных отрезках не
произойдет сбоя или отказа, приведет к нахождению степени n этого
выражения, а далее найдем предел (рис. 12.3)
Тогда p2(t)+ p3(t) = 1 - e-λ t , и разделив пропорционально частотам событий,
получим
Запишем полную вероятность успешного решения задачи (надежность),
сложив произведения вероятностей по всем путям в дереве логических
возможностей, ведущим к событиям с благоприятным исходом:
42
P = KГ P1(t) + KГ P2(t) Pвосст + KГ P3(t) Pрез = KГ(P1(t) + P2(t)Pвосст + P3(t)Pрез).
Здесь присутствуют величины, которые полностью характеризуют
организацию помехозащищенного вычислительного процесса.
Защита от сбоев . Самым надежным и испытанным приемом защиты от
последствий сбоев является двойной просчет. Он характерен для ЭВМ, не
обладающих аппаратным контролем. В случае несовпадения результатов
двойного просчета задача считается третий раз.
Практически (в АСУ) времени для этого нет. Используют методы
программного (алгоритмического) контроля. Например, пусть производится
интегрирование уравнения движения летательного аппарата с необходимой
точностью. После получения очередных значений координат производится
приближенная линейная экстраполяция для получения тех же координат на
основе предыдущих положений объекта. Тогда точно полученные
координаты должны попасть в определенную - окрестность координат,
полученных приближенно. Так производится оценка достоверности
результатов. Т.е. программно-алгоритмический контроль основан на
способности предсказания ограниченной области, которой должны
принадлежать результаты счета.
В современных ВС, как правило, используется аппаратный контроль, который
устраняет синхронные аварии и сигнализирует о асинхронных авариях,
порождая сигнал прерывания. Отметим сразу, что вероятность обнаружения
сбоя или неисправности аппаратным контролем меньше единицы (является
его характеристикой), но это не исключает применения методов, о которых
говорилось выше.
43
Как правило, использование сигнала аппаратного контроля следующее.
Программа делится на сегменты некоторого рекомендуемого объема - по
времени выполнения и по количеству команд программы, — разделенные
контрольными точками. Выход на контрольную точку сопровождается
запоминанием или дублированием всей необходимой информации для того,
чтобы следующий сегмент мог при необходимости перезапуститься
(произвести малый рестарт), если во время его выполнения произойдет
сбой. Используют при этом и расслоение памяти, запоминая необходимые
данные для рестарта в других модулях памяти, т.е. предусматривая
возможность отказов модулей памяти. Рестарт с предыдущей (т.е.
ближайшей) контрольной точки производится только в случае сбоя и в целом
требует затрат значительно меньших, чем двойной просчет и программноалгоритмический контроль.
Резервирование. Применяется во всех ответственных случаях специального
использования ВС. В дополнение к одной, основной ЭВМ используются одна
и более резервных. Резервные, как правило, не связаны с внешними
объектами или объектами управления. С ними связана основная, а они могут
дублировать ее работу, реализуя двойной просчет с анализом на совпадение
или участвуя в мажоритарной схеме контроля, когда общее число ЭВМ
больше двух и когда с помощью мажоритарного устройства на выходе всех
ЭВМ производится "голосование": результаты счета считаются правильными,
если получены большинством ЭВМ.
Иногда резервируют не отдельно ЭВМ, а весь комплекс — ЭВМ плюс
внешние устройства памяти, связи и обмена. Такой комплекс называют
линейкой.
Различают горячий и холодный резерв.
44
В горячем резерве ЭВМ работает в режиме дублирования или решения
вспомогательных задач и в любой момент готова взять функции основной. В
холодном резерве машина отключена.
Повышение характеристик надежности управляющего ВК можно видеть на
примере роста коэффициента готовности ВК. Пусть для одной ЭВМ К Г = 0,9.
Тогда использование двух (одна резервная) одинаковых ЭВМ обеспечивает
KГ(2) = 1 - (1 - KГ)2 = 0,99("две девятки");
Использование трех (две резервные) одинаковых ЭВМ обеспечивает
KГ(3) = 1 - (1 - KГ)3 = 0,999 ("три девятки").
Если в системе несколько ЭВМ, то каждая из них может иметь одну или
более резервных. Это — распределенный резерв.
Но возможен скользящий резерв, когда несколько ЭВМ являются
резервными, и каждая из них способна заменить каждую из основных.
Количество резервных ЭВМ в этом случае согласуется с λ-характеристикой
ЭВМ и количеством основных ЭВМ и приводит к созданию более надежных и
более дешевых комплексов.
Особенности обеспечения надежности ВС
45
Выше мы предположили, что для ЭВМ все сказанное бесспорно и во многом
очевидно. Переход к многопроцессорным ВС требует дополнительных
разъяснений.
Построение многопроцессорных ВС (в России — семейства МВК "Эльбрус")
привело к пересмотру всех традиционных представлений о надежности.
С одной стороны, большой объем оборудования при недостатках
элементной базы приводит к резкому возрастанию сбоев и отказов в
устройствах и модулях, с другой стороны — структурная и функциональная
избыточность, виртуализация ресурсов, управление распределением работ,
аппаратный контроль предназначены для выполнения устойчивого
вычислительного процесса.
В этих условиях подвергаются сомнению сами определения сбоя и отказа.
Эти определения принимаются по согласованию между разработчиком ВС и
системщиком, т.е. с учетом требований тех задач, которые должна решать ВС
в составе, например, системы управления.
Вышел из строя один из 10 процессоров ВС, — отказ ли это ВС? Ответ зависит
от конкретной системы, решаемых задач, временного режима их решения,
от требований к производительности, принимаемых мер по обеспечению
устойчивого вычислительного процесса и т.д. Произошел сбой процессора,
при котором сработал малый рестарт, перезапустивший весь процесс или
процесс с контрольной точки. Потери времени на этот рестарт не приведут к
необратимому нарушению работы всей системы? Т.е. это — действительно
сбой или отказ? Тем более сбои в работе ОС или та категория сбоев, которая
приводит к перезапуску ВС — к большому рестарту: загрузке ОС и работе с
начала. В "Эльбрусе-2" большой рестарт выполняется более чем за 3
секунды. К чему это отнести — к сбою или отказу? Это зависит от системы, в
которой используется ВС.
46
Таким образом, в проблемно-ориентированных ВС проблема сбоев и отказов
решается комплексно в соответствии с применением ВС.
Использование в ВС большого числа однотипных устройств с учетом идеи
виртуальных ресурсов вносит особенности и в понятие резервирования.
Реализуется структурное резервирование (развивает идеи скользящего
резервирования), на основе которого при отказах производится
реконфигурация системы: продолжение ее функционирования при
изменившемся количестве устройств одной специализации. В этом смысле
говорят о "живучести" системы.
В связи с изложенным, в МВК "Эльбрус-2" одним из механизмов,
обеспечивающих "живучесть" комплекса, является система автоматической
реконфигурации и перезапуска при сбоях и отказах (САР). Она включает в
себя специальную аппаратуру, распределенную по модулям МВК, системные
шины, программные средства ОС.
Аппаратно выполняются следующие действия:
1.Обнаружение аварии в модуле, определение ее типа, сохранение
диагностической информации и приостановка работы аварийного модуля.
2.Передача информации об аварии по специальным шинам (а мы думали,
что связь между модулями ВС — только через коммутатор!) в другие модули.
3.Обработка сигналов аварии, приходящих от других модулей и исключение
аварийного модуля из конфигурации.
4.Системная реакция на аварию: либо запуск специальных процедур ОС
(малый рестарт), либо перезапуск комплекса (большой рестарт).
Программно выполняются следующие действия:
47
1.Сбор и обработка диагностической информации аварийного модуля.
2.Попытка вернуть его в рабочую конфигурацию в предположении, что
авария — в результате сбоя.
3.Сохранение в системном журнале информации об аварии.
Таким образом, в САР предусмотрены различные реакции на разные типы
аварий.
Возникновение асинхронной аварии на процессе пользователя ведет к
автоматическому исключению неисправного модуля из конфигурации и к
запуску процедуры ОС, обрабатывающей аварийную ситуацию и
определяющей дальнейшее течение аварийного процесса — аварийное
завершение или перезапуск (малый рестарт). Остальные процессы "не
чувствуют" аварийной работы. Исключение составляет случай, когда в
конфигурации представлен лишь один модуль некоторого типа.
Возникновение в нем аварии приводит к перезапуску всего комплекса (к
большому рестарту).
Возникновение асинхронной аварии на процессе ОС всегда завершается
большим рестартом.
Оценка надежностных характеристик ВС при испытаниях
1.Согласуют контрольную задачу (КЗ) или комплекс КЗ, с помощью которых
будут производить испытание ВС. В основу КЗ берут те алгоритмы или их
аналоги, которые как можно ближе соответствуют назначению ВС в составе
системы управления. Хороших показателей испытаний добиваются тогда,
когда в состав КЗ включают модели управляемых объектов и таким образом
строят замкнутый контур управления объектом. Модели объектов могут
учитывать случайные возмущения, и, следовательно, управление становится
реальным. Такие КЗ наглядны, результативны, бесспорны, поскольку
опираются на принцип "удача — неудача".
2.Согласуют определение основных событий и состояний. Например,
48
◦малый рестарт считать обнаруженным сбоем с восстановлением;
◦"неудачный" цикл управления считать сбоем без восстановления;
◦большой рестарт считать отказом, где его время — время восстановления;
◦выход из конфигурации числа процессоров, снижающих более чем на 20%
производительность ВС (2 процессора из 10 в МВК "Эльбрус-2"), считать
отказом;
◦реконфигурацию, не снижающую более чем на 20% производительность,
считать отказом с переходом на резерв;
Аналогичные соглашения принимаются и насчет памяти, внешней памяти,
ПВВ и ППД, обеспечивающих выполнение КЗ.
3.Объявляют прогон — циклическое непрерывное решение КЗ (комплекса
КЗ) в течение значительного времени (например, 5 суток), достаточного для
набора статистики.
4.Набирают статистику по ВС в целом и по группам устройств. Обычно
разграничивают центральную часть и периферию. Непосредственно снимают
те показатели, о которых говорилось выше:
◦среднее время T0 безотказной работы;
◦среднее время Tвосст восстановления;
◦частоту λ1 сбоев; из них — число "восстановленных" для определения
Pвосст;
◦частоту λ2 отказов, из них — число приведших к успешной реконфигурации
для нахождения Pрез.
По приведенным выше формулам мы можем получить необходимые
показатели надежности
49
3
Основы многопоточных программ
3.1 Основные определения
Процессы
Процесс (process) – совокупность действий процессора и необходимых
ресурсов для обеспечения выполнения инструкций программы
Состав процесса
Области памяти данных и программ
Стек
Отображение виртуальной памяти на физическую память
Состояние
Типичные состояния процесса
Остановлен
Процесс не использует процессор
Терминирован
Процесс закончился, но ещё не удалён
Ожидает
Процесс ждёт событие
Готов
Готов к выполнению, но ожидает освобождения процессора
Выполняется
Процесс выполняется процессором
Межпроцессное взаимодействие
Межпроцессное взаимодействие – способ передачи информации между
процессами
Виды межпроцессного взаимодействия
Разделяемая память
50
Семафоры
Сигналы
Почтовые ящики
События
Событие – оповещение процесса со стороны ОС о возникновении
межпроцессного взаимодействия
Примеры событий
Принятие семафором требуемого значения
Поступление сигнала
Поступление сообщения в почтовый ящик
Потоки
Поток (поток управления, задача, нить, thread) – одна из параллельно
(асинхронно) выполняющихся ветвей процесса
Особенности потоков
В процессе присутствует единственный главный поток
Все потоки одного процесса работают в едином адресном пространстве
Общие переменные и код
Нет необходимости использовать специальные средства
взаимодействия*
Каждый поток имеет собственный стек
Каждый поток имеет собственное состояние
* В реальных условиях специальные средства межпоточного
взаимодействия обычно нужны
Дополнительные определения
Ресурс – объект (устройство, память), необходимый процессу или
потоку для выполнения заданных действий
51
Приоритет – целое число, определяющее важность каждого процесса
или потока в ОС
Чем больше приоритет у процесса или потока, тем больше
процессорного времени ему будет выделено
3.2 Виды ресурсов
Виды ресурсов по природе
Аппаратные ресурсы
Процессор
Область памяти
Периферийные устройства
Прерывания
Программные ресурсы
Программа
Данные
Файлы
Сообщения
Виды ресурсов по характеристикам
Активные
Способны изменять и создавать информацию (процессор)
Пассивные
Способны хранить информацию (память)
Локальные
Относятся к одному процессу
После завершения процесса автоматически удаляются
Разделяемые
52
Относятся к нескольким процессам
Удаляются только после окончания использования их последним
процессом
Постоянные
Для работы требуют операций «захватить» и «освободить»
Временные
Для работы требуют операций «создать» и «удалить»
Виды разделяемых ресурсов
Некритичные
Безопасно могут быть использованы одновременно несколькими
процессами и потоками
Примеры: жёсткий диск в целом, сетевая карта, видеокарта
Критичные
Безопасно могут быть использованы в один момент времени только
одним процессом или потоком
Примеры: разделяемая память при её модификации
3.3 Типы взаимодействия процессов
Типы взаимодействия процессов
Независимые процессы
Процессы не используют разделяемые ресурсы
Сотрудничающие процессы
Процессы разделяют канал коммуникации: один пишет, другой читает
Процессы работают по очереди: один работает, второй ожидает
завершения работы первого
Конкурирующие процессы
Процессы используют совместно разделяемый ресурс
53
Процессы используют критические секции
Процессы используют взаимные исключения (мьютексы)
Определения
Критическая секция – участок программного кода, который
допускается выполнять только единственным потоком (процессом)
Взаимное исключение (мьютекс, mutual exclusion, mutex) – способ
синхронизации потоков за счёт использования захвата совместно
используемого ресурса, также называемого мьютексом
Если мьютекс занят, то при попытке его захвата поток переходит в
состояние ожидания
Как только мьютекс освобождается, ранее ожидавший поток
высвобождается, а мьютекс вновь считается захваченным
Основные проблемы синхронизации процессов и потоков
Блокировка (lockout)
Поток ожидает ресурс, который не освободится никогда
Тупик (deadlock)
Два потока заблокировали каждый по своему ресурсу и ожидают
освобождения ресурса, которым владеет другой поток
Застой (starvation)
Поток монополизировал процессор
3.4 Основные средства синхронизации
Безопасное и небезопасное взаимодействие
Безопасное взаимодействие – целостность информации и неделимость
действий при взаимодействии обеспечиваются операционной системой
Небезопасное взаимодействие – целостность информации и
неделимость действий при взаимодействии обеспечиваются приложением
Разделяемая память
54
Разделяемая память – область памяти, одновременно доступная для
нескольких процессов
Это базовый вид взаимодействия процессов, к которому сводятся все
остальные виды взаимодействия
Разделяемая память может отображаться на разные области
виртуальной памяти, поэтому нужно преобразовывать указатели
Операции
Создать – создаётся объект (файл), недоступный для использования
Подсоединить – созданный объект разделяемой памяти присоединяется
к адресному пространству процесса, после этого разделяемой памятью
можно пользоваться для обмена данными
Отсоединить – объект разделяемой памяти отсоединяется от адресного
пространства процесса
Удалить – процесс сообщает ОС о том, что больше не будет
использовать разделяемую память, реально объект разделяемой памяти будет
удалён после окончания его использования последним процессом
1. Матрица следования. Является эффективным средством
синхронизации при реализации первого способа распараллеливания.
Допускает автоматическое составление в процессе диспетчирования, как
рассмотрено ранее. Легко составляется пользователем, планирующим
параллельный процесс. Для оптимального планирования требует временной
оценки работ либо оценки их сравнительной трудоемкости.
Например, пусть методом "разделяй и властвуй" производится
распараллеливание сортировки слиянием. Известны оценки сложности такой
сортировки (как функции параметра n — длины последовательности).
2. Механизм семафоров.Различают двоичные семафоры, имеющие
значения "открыт" и "закрыт", и семафоры-счетчики. "Закрытие" увеличивает
их на единицу, "открытие" — уменьшает на единицу.
Анализ задач и, в частности, задач обработки больших массивов
данных (баз данных и баз знаний) показал целесообразность совместной
реализации этих двух принципов в многопроцессорной вычислительной
55
системе. В одном семафоре, назовем его комбинированным, в
действительности воплощены два семафора: двоичный и счетчик:
В начале считывания из массива выполняется операция Сб := Сб+ 1;
при окончании считывания — операция Сб := Сб - 1. Значение Сб 0
означает: семафор C "закрыт по считыванию".
При записи в массив выполняется операция: Са := 1, "закрыть", т.е.
семафор C "закрывается по записи". При окончании записи выполняется
операция Cа := 0 — "открыть", т.е. семафор C "открывается по записи".
Однако удобства семафора-счетчика могут быть реализованы в
процедурах над двоичными семафорами. Т.е. двоичный семафор в
действительности более универсален.
Процедура ОБЪЯВИТЬ (С) объявляет список семафоров, который
рассматривается как описание, действующее внутри данного блока в
алгоритмическом языке. Этим обеспечивается выделение памяти и тип
переменной при трансляции.
Процедура ЗАКРЫТЬ (С) сначала присваивает семафорам,
перечисленным в списке C, значение "закрыт". Затем для каждого семафора
она проверяет, был ли он закрыт ранее. Если в списке C семафоров указан
один или несколько семафоров, которые были закрыты до выполнения
данной процедуры, происходит прерывание процесса (задачи). Процедура
ОС, запустившаяся по прерыванию, ставит данный процесс в очередь к тем
семафорам, которые были закрыты ранее. Таким образом, процесс
задерживается до тех пор, пока другие процессы не откроют эти семафоры.
После прерывания процесса и обработки прерывания процессор обращается
к очереди для выборки следующего задания — "готового" процесса. Если до
выполнения данной процедуры все семафоры из списка С были открыты,
выполняется следующая инструкция программы.
Процедура ЖДАТЬ (С) в случае, если в C указаны семафоры со
значением "закрыт", организует прерывание процесса. Процесс, ставший
пассивным, дополняет очереди к закрытым семафорам из списка C. Концом
56
выполнения процедуры является переход к анализу очереди "к процессору"
для последующей загрузки процессора.
Процедура ЖУЖ (С) не переводит процесс в очереди к семафорам, а
организует ожидание процессором, на котором выполняется процесс,
момента открытия всех семафоров в режиме "жужжания" — непрерывного
опроса семафоров, указанных в списке C.
Процедура ЖДАТЬ (С,Т) ограничивает время ожидания семафоров,
указанных в списке C, значением T.
Процедура ОТКРЫТЬ (С) присваивает семафорам, указанным в списке
C, значение "открыт". Процессы снимаются с очереди к указанным в C
семафорам и, если они не стоят в очереди к другим семафорам, переводятся в
очередь "к процессору" в соответствии с их приоритетом. Выполнение этих
процессов на процессорах будет продолжено с повторного выполнения тех
процедур ЗАКРЫТЬ (C') или ЖДАТЬ (C''), на которых ранее произошло
прерывание данной задачи. Таким образом, если прерывание произошло при
выполнении процедуры ЗАКРЫТЬ (C'), то всем семафорам, указанным в
списке C', вновь присваивается значение "закрыт". При этом, если среди
множества семафоров C' C окажутся такие, которые ранее были закрыты,
или другой процесс (на другом процессоре) успел закрыть семафор из C'
раньше, то выполнение данного процесса вновь прервется и он станет в
очередь к закрытым семафорам.
Процедура ПРОПУСТИТЬ (С) полностью соответствует упрощенной
версии процедуры ОТКРЫТЬ (C). Семафорам, перечисленным в списке C,
присваивается значение "открыт", и процессы из очередей к данным
семафорам переводятся в очередь "к процессору". Если процессы находились
в очереди к семафорам из С после прерывания в результате выполнения
процедур ЗАКРЫТЬ или ЖДАТЬ, то повторного выполнения этих процедур
не происходит; процессы выполняются со следующей инструкции.
Пусть в однопроцессорной ВС в режиме реального времени решаются
две задачи A и B с разной частотой решения. Задачи оформлены и
запускаются как отдельные процедуры. Задача A решается в цикле
длительности T0, задача B — в цикле длительности T1 = 4T0, но не ранее,
чем в цикле длительности T1 будет решена один раз задача A. Можно
57
предположить использование сигналов прерывания в моменты времени,
кратные T0. Однако для организации временного режима решения задач мы
воспользуемся возможностями операций над семафорами.
Будем полагать, что в моменты времени, кратные T0, но не кратные T1,
запускается управляющий процесс (супервизор) УПР0, а в моменты времени,
кратные T1, — управляющий процесс (супервизор) УПР1. Требуемая
временная диаграмма решения задач представлена на рис. 11.2.
Предположим, первоначально t0 = t1 = 0. Тогда из первых команд
УПР0 видно, что он включается в моменты
Начальные значения семафоров A и B1 — "закрыт", семафора В2 —
"открыт". Первоначально задачи A и B находятся в очереди "к процессору".
При их назначении на процессор и при выполнении первых процедур
ЖДАТЬ (А) и ЖДАТЬ (В1, В2) произойдет прерывание. После него задача A
находится в очереди к семафору A, задача B — к семафору В1. Пусть
приоритет задачи A выше приоритета задачи B. (Задачи, решаемые с
большей частотой, как правило, снабжаются более высоким приоритетом.)
В момент времени, кратный T1, задача УПР1 закрывает семафор B2, но
открывает семафоры A и B1. Задача A переводится в очередь "к процессору".
Пусть одного процессора достаточно для выполнения всех задач. Тогда с
учетом приоритета в мультипрограммном режиме задача В решается в
промежутки времени, не занятые решением других задач. После выполнения
задачи УПР1 начинается выполнение задачи A с закрытия семафора A. Так
как до выполнения процедуры ЗАКРЫТЬ(А) семафор А имел значение
"открыт", то прерывания выполнения задачи A не произойдет и она будет
решена до конца. В конце ее решения по процедуре ПРОПУСТИТЬ(В2)
откроется семафор B2 и задача B перейдет в очередь "к процессору". Задача
A организована по принципу зацикливания, т.е. после ее окончания
управление передается на ее начало с процедуры ЗАКРЫТЬ(А). Так как к
моменту выполнения этой процедуры семафор A закрыт в результате ее
предыдущего выполнения, произойдет прерывание и постановка задачи A в
очередь к семафору A. Этот семафор откроется только при выполнении
задачи УПР0 после увеличения текущего времени на T0. При этом, т.к.
открытие семафора A в задаче УПР0 производится процедурой ОТКРЫТЬ, а
прерывание задачи A произошло по процедуре ЗАКРЫТЬ, то выполнение
задачи A продолжится с выполнения процедуры ЗАКРЫТЬ(А). Таким
образом, более чем однократное решение задачи A без прерывания вновь
окажется невозможным.
58
Аналогично по принципу зацикливания организовано и решение задачи
B. После однократного выполнения она будет прервана при повторном
выполнении процедуры ЗАКРЫТЬ(В1). Семафор B1 откроется только при
следующем выполнении задачи УПР1 после увеличения текущего времени
на T. При этом выполнение задачи B продолжится с повторного выполнения
процедуры ЗАКРЫТЬ(В1), т.к. семафор B1 открывается процедурой
ОТКРЫТЬ. Однако продолжение выполнения задачи B станет возможным
после открытия семафора B2. Он же окажется открытым только после
однократного выполнения задачи A в цикле длительности T1 после
выполнения процедуры ПРОПУСТИТЬ(В2) в конце решения задачи A.
Использования этой процедуры в данном случае достаточно.
Таким образом, соблюдается требуемый порядок решения задач.
Теперь усложним пример. Предположим, что для решения задачи A
одного процессора недостаточно и необходимо использовать возможности ее
параллельного решения. Мы представляем эту задачу в виде частично
упорядоченного множества информационно- взаимосвязанных работ —
подзадач — и используем механизм семафоров для соблюдения порядка
следования этих работ. При этом мы предполагаем, что в ВС реализовано
децентрализованное диспетчирование, т.е. свободные процессоры
обращаются за очередным заданием к очереди "к процессору". На рис. 11.4
представлена примерная граф-схема задачи на основе составляющих ее
подзадач. Каждая вершина соответствует подзадаче. Стрелки определяют
информационную преемственность. Процедура над семафорами,
изображенная выше вершины, показывает, что соответствующая подзадача
начинается с выполнения этой процедуры. Процедура, изображенная ниже
вершины, показывает, что подзадача заканчивается выполнением этой
процедуры.
Семафоры
Семафор – это объект синхронизации, задающий количество процессов
и/или потоков, имеющих одновременный доступ к разделяемому ресурсу
По сути это безопасный счётчик
Значение счётчика может быть меньше 0 – это значит, что несколько
потоков/процессов ожидают освобождения семафора
59
Виды
Двоичные (булевские)
Механизм защиты критичного ресурса
Начальное значение счётчика равно 1
Счётные
Механизм защиты ресурса, который можно одновременно
использовать нескольким потокам/процессам
Начальное значение счётчика больше 1 – определяет количество
процессов/потоков, имеющих доступ к ресурсу
Операции с семафорами
Взять (Get) – уменьшает значение счётчика на k, если в счётчике
значение не меньше k, иначе поток блокируется
Вернуть (Put) – увеличивает значение счётчика на k
Попробовать взять (TryGet) – то же, что и Get, но не блокирует поток
Проверить (Test) – возвращает значение счётчика
Блокировать (Lock) – «обнулить», если значение счётчика больше 0
Разблокировать (Unlock) – вернуть столько, сколько сняли при
блокировке
События
Событие – логический сигнал (оповещение), приходящий асинхронно
по отношению к ходу процесса/потока
Состав
Булевская переменная: 1 – событие пришло, 0 – событие не пришло
Очередь потоков/процессов, ожидающих приход сообщения
Операции
Послать (Signal) – переменная события устанавливается в 1,
активизируется первая задача из очереди ожидания
60
Послать всем (Broadcast) - переменная события устанавливается в 1,
активизируются все задачи из очереди ожидания
Ожидать (Wait) – поставить задачу в очередь ожидания
Сбросить (Reset) – установить переменную события в 0
Проверить (Test) – узнать значение переменной события
3. Передача сообщений, "почтовый ящик". Метод "почтовых ящиков"
является распространенной формой метода передачи сообщений.
Каждому процессу выделяется массив — "почтовый ящик", в который
другие процессы направляют свои результаты или сигналы, необходимые для
выполнения или запуска этого процесса.
Возможна реализация виртуальных процессоров. Свободный процессор
опрашивает очередь подряд, т.е. в соответствии с невозрастанием
приоритетов, и пытается запустить тот процесс, для которого в его "почтовом
ящике" есть вся необходимая для этого информация. Либо же этот анализ
может производить сам запускаемый процесс: если всей необходимой
информации в его "почтовом ящике" нет, процесс прерывается и
возвращается в очередь. Процессор продолжает циклический опрос очереди.
С помощью "почтовых ящиков" реализуется схема управления потоком
данных (data flow), когда явное задание исходной информации для процесса
является необходимым условием его запуска.
4. Механизм закрытия адресов. Осуществляется командой вида
"закрыть адрес (по считыванию)". Адрес запоминается в специальном ЗУ, и
при попытке считывания по нему производится ожидание — по аналогии с
процедурой ЖУЖ. Открывается адрес по записи по нему. Для избежания
тупиковых ситуаций, например,
ЗАКР a
СЧИТАТЬ a
61
указывается номер того процессора, который закрыл адрес. Тогда есть
возможность принять какое-то решение. Например, разрешить процессору,
закрывшему адрес, открыть его.
Вернемся к примеру счета способом "пирамиды". Наметилось решение
проблемы синхронизации: пусть счет очередного элемента предваряется
командой, закрывающей адрес результата — считаемого элемента. Засылка
найденного элемента откроет адрес. Тогда, пока этот элемент не будет
получен, его адрес не сможет использоваться для счета других элементов.
Такой способ синхронизации был приведен при рассмотрении
архитектуры SPMD.
5. Механизм активного ожидания.
Он основан на том, что к разделяемой (т.е. к общедоступной) ячейке
ОП одновременно может обращаться по записи или считыванию только один
процессор. При этом конфликт разрешается аппаратно: один обращается,
остальные ждут. Тогда в ячейках можно хранить информацию о состоянии
процессов. Процессы их могут анализировать и изменять.
Пример рассмотрим далее, совместив его с рассмотрением одной из
задач синхронизации.
6. Монитор.
Монитор - скорее сервисное средство, позволяющее пользователю
избежать заботы о синхронизации использования разделяемых ресурсов. Это
программный компонент, в котором разделяемые переменные
(представленные именами разделяемых ресурсов) определены как локальные
переменные монитора. Определены операции над этими переменными процедуры монитора. Одновременно только один процесс работает с
монитором! Когда процессу требуется работать с разделяемой переменной,
активизируется соответствующая процедура монитора. Если при обращении
62
процесса к монитору ресурс занят, то вызывающий процесс должен быть
задержан.
С каждой причиной задержки процесса связана специальная
переменная типа CONDITION (условие), и задерживаемый процесс ставится
в очередь к некоторой переменной этого типа.
К переменным указанного типа применяются операции WAIT и
SIGNAL. Операция WAIT ставит процесс в очередь к данной переменной
(например, указывающей на занятость соответствующего ресурса). Операция
SIGNAL позволяет активизировать процесс (в случае, когда условие
выполнено).
Задачи синхронизации при использовании разделяемых ресурсов
Основные понятия
Объектами распараллеливания являются неделимые процессы —
работы, на которые разбивается исходный алгоритм или разрабатываемая
программная система.
Процессы используют различные устройства, данные, компоненты
программного обеспечения, которые называются ресурсами системы.
Ресурсы, используемые несколькими процессами, называются
разделяемыми.
Разделяемые ресурсы, которые одновременно могут использоваться не
более чем одним процессом, называются критическими ресурсами.
(Например, очередь "к процессору" — критический ресурс.)
Участки программы (процесса), где процессы обращаются к
разделяемому ресурсу, называются критическими интервалами (блоками,
секциями).
63
Процессы называются взаимосвязанными, если они используют общие
критические ресурсы.
Процессы называются информационно-взаимосвязанными, если они
обмениваются информацией. Процессы называются взаимосвязанными по
управлению, если один из них вырабатывает условия для активизации
другого (других). Ограничения на порядок выполнения процессов
называются синхронизацией процессов. Отношения между процессами
задаются правилами синхронизации. Для задания правил синхронизации
используются механизмы (средства) синхронизации (рассмотренные выше
матрицы следования, семафоры и др.) Задача синхронизации — это задача, в
рамках которой требуется согласовать выполнение нескольких процессов.
Решение таких задач достигается с помощью механизмов синхронизации. С
задачами синхронизации связывают тупики (тупиковые ситуации). Тупик —
это взаимная блокировка процессами друг друга, при которой их выполнение
не может быть продолжено, а также самоблокировка.
Например, процессу A необходимы внешние устройства X, Y. С
устройством Y пока работает процесс B. Тогда процесс A "захватывает" пока
устройство X и ждет освобождения Y. Но устройство X потребовалось и
процессу B, который также "зависает" в ожидании его освобождения.
Под общим тупиком подразумевается взаимная блокировка всех
взаимосвязанных процессов. Под локальным тупиком понимается
бесконечная блокировка одного из процессов при попытке войти в
критический интервал.
Выше был приведен пример:
1."Закрыть адрес по считыванию" a
2."Считать по адресу" а
Здесь возможен локальный тупик — бесконечное выполнение команды
(2), если другой процесс (на другом процессоре) или ОС не "откроет" адрес а.
Очереди сообщений (почтовые ящики)
64
Очередь сообщений (pipe) – объект обмена данными между потоками,
устроенный в виде FIFO
Состав
Собственно, очередь сообщений
Очередь потоков, ожидающих сообщения
Очередь потоков, ожидающих освобождения места в почтовом ящике
Mutex для доступа к одним и тем же сообщениям в почтовом ящике из
разных потоков
Операции очередей сообщений
Положить (Push)– положить сообщение в конец очереди, если очередь
заполнена, то поток блокируется
Попробовать положить (TryPush) – то же, что и Push, но поток не
блокируется
Положить в начало (PushStart) – положить сообщение в конец очереди,
если очередь заполнена, то поток блокируется
Попробовать положить в начало (TryPushStart) – то же, что и PushStart,
но поток не блокируется
Взять (Pop) – взять сообщение, если очередь пуста, поток блокируется
Попробовать взять (TryPop) – то же, что и Pop, но поток не блокируется
Очистить (Clear) – очистить очередь
Mutex
Mutex (mutual exclusion) – механизм синхронизации, предназначенный
для устранения недостатков семафоров
Захват семафора после его случайного разблокирования всегда удачен
и может привести к повреждению данных
Семафор является объектом ОС, поэтому для доступа к нему
необходимо переключать задачи
Состав мьютекса
Булевский семафор
65
Идентификатор потока, захватившего разделяемый ресурс
Мьютексы хранятся в памяти процесса (локальные) или в разделяемой
памяти процессов (глобальные)
Операции
Захватить (Lock) – если мьютекс захвачен другим потоком, текущий
поток блокируется
Освободить (Unlock) – работает только для потока, являющегося
владельцем мьютекса
Попробовать захватить (TryLock) – то же, что и Lock, но текущий
поток не блокируется
Условные переменные – condvar
Condvar (Conditional Variable) – средство синхронизации,
предназначенное для ожидания потоком некторого условия
Является событием, дополненным мьютексом
Ожидать (Wait) – {мьютекс должен быть заблокированным}
освободить мьютекс, ожидать событие, захватить мьютекс
Послать (Send) – послать событие, если нет потоков, ожидающих
событие, ничего не произойдёт
Послать всем (Broadcast) – то же, что и Send, но используется Послать
всем (Broadcast) для события
Задачи синхронизации
Известны типичные задачи синхронизации, к которым могут быть
сведены практически все известные способы упорядочения работ во времени.
Эти задачи, как правило, решаются при реализации второго способа
распараллеливания.
1. Задача взаимного исключения. Имеется несколько процессов,
программы которых содержат критические интервалы, где процессы
обращаются к одному разделяемому критическому ресурсу (например, к базе
знаний). Требуется исключить одновременное вхождение в такие
критические интервалы более чем одного процесса.
66
Требования к решению этой задачи:
•задержка любого процесса вне его критического интервала не должна
влиять на развитие других процессов;
•решение должно быть симметрично относительно процессов;
•решение не должно допускать тупиков.
Решение с помощью механизма семафоров:
Для сравнения приведем альтернативный способ решения с помощью
механизма активного ожидания.
Выделим ячейку памяти C, в которую будем записывать 0, если ни
один из процессов не требует доступа к критическому ресурсу, и номер i того
процесса (или выполняющего процессора), который вступил в свой
критический интервал. Тогда условием вхождения i-го процесса в
критический интервал является результат выполнения следующего
оператора:
Действительно, если (C) = 0, то процесс может войти в критический
интервал. Однако возможно, что через небольшое время другой процесс (на
другом процессоре) тоже сделал такую же проверку и следом за первым
произвел засылку в C своего номера. Поэтому требуется повторная проверка
того, что в C находится именно номер данного процесса. При положительном
результате повторного анализа процесс может вступить в критический
интервал, т.е. занять критический ресурс. После выполнения критического
интервала в C засылается 0.
С помощью механизма семафоров такая синхронизация выполняется
значительно проще, т.к. многие действия предусмотрены внутри процедур их
обработки.
67
2. Задача "поставщики — потребители". Имеется ограниченный буфер
на несколько порций информации. Он является критическим ресурсом для
процессов двух типов:
•процессы "поставщики", получая доступ к ресурсу, помещают на
свободное место в буфере несколько или одну порцию информации;
•процессы "потребители", получая доступ к ресурсу, считывают из него
порции информации.
Требуется исключить одновременный доступ к ресурсу любых двух
процессов. При опустошении буфера следует задерживать процессы
"потребители", при полном заполнении буфера — процессы "поставщики".
Эта задача возникает, например, при обмене с внешними устройствами
и заключается в программной имитации кольцевого или бесконечного
буфера.
Возможная схема решения задачи с помощью семафоров:
Тогда уточним данную процедуру с учетом используемых индикаторов
считывания и заполнения.
3. Задача "читатели — писатели".
Имеется разделяемый ресурс — область памяти, к которой требуется
доступ процессам двух типов:
Процессы первого типа — "ЧИТАТЕЛИ" — могут получать доступ к
разделяемому ресурсу одновременно. Они считывают информацию.
Процессы второго типа — "ПИСАТЕЛИ" — взаимно исключают и друг
друга, и "читателей". Они записывают в разделяемую область памяти
данные.
68
Задача известна в двух вариантах:
1."Читатели", изъявившие желание получить доступ к ресурсу, должны
получить его как можно быстрее; это — первая задача ЧП.
2.2. "Читатели", изъявившие желание получить доступ к ресурсу,
должны получить его как можно быстрее, если отсутствуют запросы от
"писателей". "Писатель", требующий доступ к ресурсу, должен получить его
как можно быстрее, но после обслуживания "читателей", подошедших к
ресурсу до первого "писателя". Это — вторая задача ЧП.
Приведем возможное решение задач с помощью комбинированного
семафора.
Считаем, что процедура ОТКРЫТЬ ПО СЧИТЫВАНИЮ выполняется
подобно процедуре ПРОПУСТИТЬ, изменяя только значение семафорасчетчика. Процедура ОТКРЫТЬ ПО ЗАПИСИ выполняется подобно
процедуре ОТКРЫТЬ, "открывая" семафор и обеспечивая запуск
"задержанных" процессов с процедур ЗАКРЫТЬ ПО ЗАПИСИ или ЖДАТЬ
ПО ЗАПИСИ, при выполнении которых произошло прерывание.
Тогда критические интервалы для каждой задачи могут быть
выполнены по следующим схемам.
4. Задача "обедающие философы". За круглым столом сидят k
философов, которые проводят время, чередуя философские размышления с
потреблением пищи. Перед каждым — тарелка спагетти, между тарелками —
по одной вилке. Для еды каждому философу требуются две вилки.
Использовать можно только вилки, лежащие рядом с тарелками. Так как
переходы от размышления к принятию пищи производятся в
непредсказуемые моменты времени, то возможны конфликты и требуется
синхронизация процессов.
Представим следующую модель, требующую решение данной задачи,
— модель оперативного обмена между процессорами векторной ВС или
строк (столбцов) матричной
69
Например, после счета очередного элемента в узле сетки результаты
должны быть переданы соседним процессорным элементам для
использования в следующей итерации. Очевидна возможность конфликтов
при попытке одновременной встречной передачи.
Пусть с i-м процессором для передачи влево связан "левый" семафор
Ci, для передачи вправо — "правый" семафор Ci+1 (или наоборот). Пусть
каждый процессор, нуждающийся в передаче двум соседям, пытается
сначала закрыть свой "правый" (аналогично, "левый") семафор. Затем, если
это не успел сделать левый сосед, он попытается закрыть "левый" семафор и
произвести передачу. Тогда возможен общий тупик, если все процессоры
одновременно закроют все семафоры. (При одновременном пересчете
значений функции в узлах сетки вероятность такой ситуации высока.)
Разрешим четным процессорам сначала закрывать "левые" ("правые")
семафоры, а нечетным — "правые" ("левые"). Тогда схемы программ для них
будут выглядеть следующим образом:
Пусть процессор с нечетным номером i нуждается в обмене — влево и
вправо. Он пытается выполнить процедуру ЗАКРЫТЬ(Ci+1). Предположим,
что этот семафор (для него — "правый") закрыт i+1-м процессором. Но для
этого процессора с четным номером этот семафор — также первый в порядке
закрытия ("левый"). Следовательно, он либо ждет возможности закрытия
своего "правого" семафора, либо ведет обмен. Если он ждет своего "правого"
семафора, то он его дождется, т.к. он — второй для процессора i+2, ведущего
обмен. Значит, этот процессор закончит обмен и откроет свой "левый"
семафор. Тогда процессор i+1 выполнит обмен и откроет семафор Ci+1.
Тогда и процессор i, наконец, сможет выполнить необходимую процедуру.
После этого он попытается выполнить процедуру ЗАКРЫТЬ(Ci). Этот
семафор является "правым", т.е. вторым для процессора с четным номером i1. Следовательно, этот процессор закрыл оба связанных с ним семафора и
ведет обмен. По окончании обмена он откроет семафоры, и процессор i
дождется необходимого "левого" семафора и сможет его закрыть для себя.
Таким образом, тупиковая ситуация возникнуть не может.
Обычно n — степень двойки. Если же n нечетно, то на границе n ↔ 1
взаимодействуют два "нечетных" процесса обмена. Здесь возможна
70
блокировка, когда процессор 1 закроет C2, а процессор n — семафор C1 (1 =
(n+1)mod n). Однако, процессор n обязательно дождется открытия семафора
Cn и выполнит обмен. Значит и процессор 1 дождется открытия семафора C1,
выполнит обмен и откроет C2. Так что тупики и в данном случае исключены.
Рассмотрение данной задачи синхронизации выводит нас за рамки
традиционного применения мультипроцессорной системы типа MIMD, к
каким относятся, например, ВС семейства "Эльбрус". Однако
универсальность такой архитектуры допускает принципиальную
возможность воспроизведения на ней более "жестких" архитектур —
матричных и векторных, т.е. типа SIMD. Реализация метода "сеток" на
матричных ВС или на структурах типа "гиперкуб" требует подобной
передачи не только двум соседним процессорам по строке, но и соседним по
столбцу, по диагонали и т.д. Значит, возможно обобщение данной задачи и
алгоритма синхронизации. Указанный выше прием разделения процессоров
на четные и нечетные может быть применен по всем направлениям обмена —
по строкам, по столбцам, по диагонали и т.д.
5.Задача обновления данных. Предполагает запрет на использование
обновляемых данных. Например, процессор обновляет запись в базе данных.
В простейшем случае может быть использован признак, по которому
запрещается обращение к данной записи, пока она не будет обновлена.
(Конечно, мы не рассматриваем того примитивного решения, когда БД
превращается в ресурс с последовательным доступом. Мы стремимся к
многоканальному, параллельному доступу.)
Вместо признака может быть использован семафор. Ожидание может
быть организовано процедурами ЖДАТЬ или ЖУЖ.
71
4
Реализация многопоточности на различных платформах
4.1 BSD, UNIX System V, POSIX, XPG
BSD
BSD – Berkeley Software Distribution (Berkeley UNIX) – система
распространения программного обеспечения в исходных кодах, созданная
для обмена между учебными заведениями.
Разработки производились с 1977 по 1995 годы в университете
Калифорнии.
Эталонная реализация UNIX в BSD оказала большое влияние на
развитие многих операционных систем
 UNIX
 Windows
 Mac OS X
 Введены
 Виртуальная память (VAX – Virtual Memory UNIX)
 TCP/IP
 NFS
UNIX System V
UNIX System V (SysV) – одна из первых успешных реализаций UNIX,
выполненная компанией AT&T.
Считается эталонной реализацией, оказала большое влияние на POSIX.
Введены межпоточные взаимодействия внутри процесса:
 Сообщения
 Семафоры
 Общая память
POSIX
POSIX – (Portable Operating System Interface for Unix – переносимый
интерфейс операционных систем Unix) – набор стандартов,
72
описывающих интерфейсы между операционной системой и
прикладной программой.
Разработано комитетом 1003 IEEE, утверждено стандартом ISO/IEC
9945.
POSIX 1003.1 – Корневые службы
 Создание и контроль процессов
 Сигналы
 Исключения плавающей точки
 Нарушение сегментации
 Запрещенные директивы
 Ошибки шины
 Таймеры
 Операции над файлами (директориями)
 Каналы
 Библиотека C (включает стандарт ANSI C)
 Интерфейс и контроль ввода/вывода
POSIX 1003.1b – Расширения реального времени
 Планировка приоритетов
 Сигналы реального времени
 Таймеры и синхронизация
 Семафоры
 Передача сообщений
 Общая память
 Асинхронный и синхронный ввод/вывод
 Интерфейс блокировки памяти
POSIX 1003.1c – Расширения потоков
 Создание, контроль и очистка потоков
 Планировщик потоков
 Синхронизация потоков
 Обработка сигналов
Поддержка POSIX операционными системами
 Полная поддержка
o Android OS
o LynxOS
o QNX
o VxWorks
 Неполная поддержка
o FreeBSD
o Linux
73
o Symbian OS
 POSIX для Windows NT
o Поддерживается только POSIX 1003.1
http://msdn.microsoft.com/en-us/library/y23kc048(jajp,VS.90).aspx
o Для расширения поддержки POSIX используется Interix
 http://support.microsoft.com/kb/324081
 http://msdn.microsoft.com/en-us/library/bb463220.aspx
XPG
XPG – X/Open Portability Guide – стандарт на ОС UNIX, являющийся
более общим, чем POSIX. Определяет требования к операционной
системе (тому, что она должна содержать)
4.2 WinAPI
Возможности по работе с потоками и процессами
 Работа с процессами
 Работа с потоками
 Поддержка 32-разрядных и 64-разрядных потоков
 Объединение совместно выполняющихся процессов в задания
(Job)
 Пулы потоков
 Служба поддержки порядка потоков (Thread Ordering Service) –
обеспечивает однократное выполнение потоков в нужном
порядке в течение заданного периода
 Служба планировщика мультимедийного класса (Multimedia
Class Scheduler Service) – обеспечивает максимально допустимое
использование процессора для мультимедийных приложений без
запрета выделения ресурсов низкоприоритетным приложениям
 Волокна (fiber) – возможность самостоятельно планировать
выполнение модулей выполнения
 Поддержка NUMA – (non-uniform memory access) – архитектуры
многопроцессорных систем, в которой каждый процессор
обладает собственной памятью и обменивается с остальными
процессорами посредством кэшируемой шины
 Работа с логическими процессорами и группами процессоров для
поддержки более 64 логических процессоров
 Пользовательский планировщик (User-Mode Scheduling UMS) –
возможность написать собственный планировщик для управления
переключением потоков
74
Управление потоками
 Общие операции
o CreateThread
o OpenThread
o SuspendThread
o ResumeThread
o GetCurrentThreadId
o GetExitCodeThread
o ExitThread – предпочтительный способ завершения потока
o TerminateThread – опасна, стек потока не освобождается
o WaitForSingleObject – ожидание завершения потока
o GetTickCount – возвращает количество тиков (мс) с момента
старта ОС
o GetThreadTimes
o Sleep
o SuspendThread
 Управление приоритетом
o SetThreadPriority
o GetThreadPriority
o SetThreadPriorityBoost – разрешение или запрет
динамического повышения приоритета потока (для
активной задачи)
o GetThreadPriorityBoost
 Задание приоритетного процессора
o SetThreadAffinityMask – на основе маски соответствия
потока группе процессоров
o SetThreadIdealProcessor – по номеру процессора
 Список функций работы с процессами и потоками
http://msdn.microsoft.com/en-us/library/ms684847(v=VS.85).aspx
 Список функций обеспечения синхронизации
http://msdn.microsoft.com/en-us/library/ms686360(v=VS.85).aspx
 Использование разделяемой памяти http://msdn.microsoft.com/enus/library/aa366878(v=VS.85).aspx
 Пулы потоков http://www.rsdn.ru/article/baseserv/threadpool.xml
 Особенности реализации многозадачности
http://www.dtf.ru/articles/read.php?id=39888
 Критические секции http://www.rsdn.ru/article/baseserv/critsec.xml
75
4.3 .NET
Потоки
Для создания потока необходимо указать метод, используемый для
выполнения потока.
 Основные операции
o Запуск
o Ожидание завершения
o Запрос досрочного завершения
Пулы потоков
 Запросы сервера удобно обрабатывать в отдельных потоках
 Большое количество запросов – большое количество потоков –
медленная работа
 Необходимо ограничить количество потоков – так появился пул
потоков
Таймеры




Выполнение периодически повторяющихся операций
Выполнение операций по истечении указанного интервала времени
Для выполнения операции таймера используется отдельный поток
Виды таймеров
o System.Threading.Timer – обычный
o System.Windows.Forms.Timer – «оконный», без создания
дополнительного потока
o System.Timers.Timer – устаревший класс
Модель асинхронного программирования
 Asynchronous Programming Model (APM)
 Используется для выполнения длительных операций в асинхронном
режиме
 Применимо к System.IO.Stream, System.Net.Sockets.Socket,
System.Data.SqlClient.SqlCommand и других
 Пары методов: BeginXxx, EndXxx
Стыковка в модели асинхронного программирования
 Ожидание завершения
o Метод EndXxx
 Регулярный опрос (поллинг)
o IAsyncResult.IsComplete
76
o IAsyncResult.AsyncWaitHandle
 Использование метода обратного вызова
Асинхронное выполнение синхронных операций
 Используются асинхронные делегаты
 Синхронному методу сопоставляется делегат
 Делегат вызывается асинхронно
o Метод BeginIvoke
 Ожидание завершения асинхронной операции
o Метод EndInvoke
 Дополнительная информация http://msdn.microsoft.com/enus/library/2e08f6yc.aspx
Обработка исключений
 Исключение может произойти в любом потоке
 Исключение будет выбрасываться повторно
o При вызове EndXxx
o При вызове EndInvoke
Контекст выполнения потока
 Контекст включает
o Параметры безопасности
o Параметры локализации
o Параметры транзакций
 Контекст занимает много места и копируется в создаваемые потоки
 Класс ExecutionContext позволяет управлять копированием контекста
Volatile
 У процессора есть кэш, ускоряющий работу с памятью
o Данные читаются в кэш из памяти
o Данные модифицируются в кэше
 При совместном использовании памяти несколькими ядрами каждое
ядро имеет свой кэш
 Операции volatile делают кэш недействительным
 Доступные возможности по использованию volatile
o Операции VolatileRead, VolatileWrite
o Объявление переменных volatile
77
Singleton
 Ограничение использования единственного объекта множеством
потоков
o Паттерн Singleton или Lazy Initialization
 Дополнительная информация http://msdn.microsoft.com/enus/library/ff650316.aspx
ReaderWriterLock
 Обеспечивает синхронизацию при обращении к переменной, если
o Одни потоки только пишут в неё
o Другие потоки только читают из неё
Апартаменты (COM)
 Апартамент – модель создания и использования объектов
 STA – Single Thread Apartment реентерабельность не обеспечивается –
каждый объект создаётся в отдельном потоке
 MTA – Multiple Thread Apartment реентерабельность обеспечена – один
объект может использоваться повторно и одновременно в каждом
потоке приложения
 Дополнительная информация http://msdn.microsoft.com/enus/library/ms809971.aspx
4.4 Java
Работа с процессами и потоками
 Создание процессов
o Класс java.lang.ProcessBuilder
o После создания потока его можно принудительно завершить или
послать ему данные
 Создание потоков
o Наследование от класса java.lang.Thread – основной
o Реализация интерфейса java.lang.Runnable
Пример создания процесса в Java находится в приложении 2.
Способы синхронизации потоков
 Ключевое слово synchronized – критическая секция, основной
o Объявление метода, целиком являющегося критической секцией
78
o Выделение критической секции в теле метода
 Использование типов пакета java.util.concurrent
o CyclicBarrier
o CountDownLatch
 ConcurrentMap<K,V>
 BlockingQueue<E>
Модель работы с потоками
 В JRE до версии 1.2 использовалась модель Green Threads
o Green Threads – имитация потоков операционной системы
o JVM берёт на себя заботу о переключении между разными Green
Threads
o Для ОС все Green Threads управляемого процесса являются
единственным потоком
o Даже при использовании Green Threads для ОС JVM
представляется в виде нескольких потоков, например, потока, в
котором запускается сборщик мусора
 Преимущества
o Теоретически выше скорость переключения между Green Threads
(реально работает очень медленно)
o Требуется меньше ресурсов
o Возможность создания большего количества Green Threads, по
сравнению с потоками
 Недостатки
o Невозможно выполнить Green Threads на разных процессорах
o В единицу времени выполняется только один Green Thread
 Начиная с версии 1.2 в JRE по умолчанию используется модель
потоков ОС, но модель Green Threads также поддерживается
Особенности работы с потоками Java в ОС Solaris
 В ОС Solaris для Java начиная с версии 1.2 возможна использование
модели MхN для работы с потоками
79
Рисунок 19. Java Solaris M:N модель
Управление задачами
 java.util.concurrent.ThreadPoolExecutor – реализация пула потоков
 java.util.concurrent.ScheduledThreadPoolExecutor – дополнительная
возможность выполнять задания по расписанию или периодически
80
5
Управление и синхронизация процессов и потоков
5.1 Функции создания процессов и управления ими
Функция fork
 pid_t fork(void);
 Вызов функции приводит к созданию почти полной копии текущего
процесса, запущенного с момента вызова функции fork
 Возвращаемое значение – идентификатор потока, позволяющий
определить, является ли выполняющийся процесс исходным или
порождённым
o В исходном процессе возвращается идентификатор
порождённого процесса
o В порождённом процессе возвращается 0
o Возвращает -1 в случае, если создать дополнительный процесс не
удалось (код ошибки заносится в errno)
 Отличия порождённого процесса от исходного
o идентификатор процесса (PID, PPID)
o израсходованное время ЦП (оно обнуляется в момент создания)
o сигналы процесса-родителя, требующие ответа
o заблокированные файлы (record locking)
 Прототип функции описан в файле unistd.h
 Тип возвращаемого значения описан в файле sys/types.h
 Дополнительная информация
http://www.opengroup.org/onlinepubs/009695399/functions/fork.html
Семейство функций exec и переменная environ
 int execl(const char *path, const char *arg0, ... /*, (char *)0 */);
 int execv(const char *path, char *const argv[]);
 Запуск нового процесса и завершение выполнения текущего процесса:
замена текущего процесса новым
 Возвращаемое значение
o В случае ошибки -1 (код ошибки заносится в errno)
o В случае успеха возвращаемое значение отсутствует, поскольку
текущий процесс успешно завершается
 Отличия между вариантами функции в её семействе определяются
набором параметров
 Прототипы функций описаны в файле unistd.h
 extern char **environ; – дополнительно используемая глобальная
переменная
o Инициализируется как указатель на массив указателей на строки
переменных среды (environment strings)
81
 Дополнительная информация
http://www.opengroup.org/onlinepubs/009695399/functions/exec.html
Семейство функций waitpid
 pid_t wait(int *stat_loc);
 pid_t waitpid(pid_t pid, int *stat_loc, int options);
 Ожидание завершения одного или нескольких дочерних процессов
 Функция позволяет получить информацию только об одном
завершённом процессе, для получения информации о нескольких
процессах функцию нужно вызвать несколько раз
 Варианты работы в зависимости от значения параметра pid
o pid = -1: выполняется ожидание завершения любого дочернего
процесса (вызов waitpid эквивалентен вызову wait)
o pid > 0: выполняется ожидание завершения дочернего процесса с
указанным идентификатором
o pid = 0: выполняется ожидание завершения дочернего процесса с
идентификатором группы процесса, равным идентификатору
группы текущего процесса
o pid < -1: выполняется ожидание завершения дочернего процесса с
идентификатором группы процесса, равным идентификатору
группы, заданному абсолютным значением pid
 Параметр stat_loc содержит указатель на информацию о причине
завершения дочернего процесса, извлекаемую с помощью макросов
 Параметр options задаётся в соответствии со значениями именованных
констант, позволяет обеспечить неблокирующий вызов функции и
отфильтровать завершённые процессы, по которым уже возвращалось
состояние в предыдущих вызовах функции
 Возвращаемое значение
o В случае успеха – идентификатор завершившегося процесса
o В случае ошибки -1 (код ошибки заносится в errno)
 Прототип функции описан в файле sys/wait.h
 Тип возвращаемого значения описан в файле sys/types.h
 Дополнительная информация
http://www.opengroup.org/onlinepubs/009695399/functions/waitpid.html
Функция kill
 int kill(pid_t pid, int sig);
 Посылает сигнал процессу или группе процессов
 Варианты работы в зависимости от значения параметра pid
o pid = -1: выполняется отправка сигнала всем процессам (за
исключением системных процессов), к которым допустимо
отправлять сигналы текущему процессу
82
o pid > 0: выполняется отправка сигнала процессу с указанным
идентификатором
o pid = 0: выполняется отправка сигнала процессам с
идентификатором группы процесса, равным идентификатору
группы текущего процесса
o pid < -1: выполняется отправка сигнала процессам с
идентификатором группы процесса, равным идентификатору
группы, заданному абсолютным значением pid
 Прототип функции описан в файле signal.h
 Дополнительная информация
http://www.opengroup.org/onlinepubs/009695399/functions/kill.html
Функция signal
 void (*signal(int sig, void (*func)(int)))(int);
 Задаёт функцию обратного вызова для обработки поступившего
сигнала текущему процессу
 Параметр sig определяет значение сигнала, для которого назначается
обработчик
 Варианты работы в зависимости от значения параметра func
o SIG_DFL – к указанному сигналу подключается обработчик по
умолчанию
o SIG_IGN – процесс не обрабатывает поступление указанного
сигнала
o Указатель на функцию – к указанному сигналу подключается
обработчик в виде заданной указателем функции
 Возвращаемое значение – указатель на функцию обратного вызова,
являющуюся старым обработчиком сигнала
 Прототип функции описан в файле signal.h
 Дополнительная информация
http://www.opengroup.org/onlinepubs/009695399/functions/signal.html
5.2 Функции создания потоков и управления ими
Функция pthread_create
 int pthread_create(
pthread_t *restrict thread,
const pthread_attr_t *restrict attr,
void *(*start_routine)(void*),
void *restrict arg);
 Функция создания потока
 Параметры
o thread – идентификатор созданного потока (выходной параметр)
o attr – указатель на структуру с атрибутами создаваемого потока
83
o start_routine – указатель на функцию обратного вызова,
определяющую создаваемый поток
o arg – аргумент, передаваемый в функцию обратного выхова
 Прототип функции описан в файле pthread.h
 Дополнительная информация
http://www.opengroup.org/onlinepubs/009695399/functions/pthread_create.
html
Функция pthread_exit
 void pthread_exit(void *value_ptr);
 Завершает выполнение потока, в контексте которого она вызвана
 Параметр value_ptr передаётся в функцию int pthread_join(pthread_t
thread, void **value_ptr), вызываемую для ожидания завершения потока
в другом потоке
 Прототип функции описан в файле pthread.h
 Дополнительная информация
http://www.opengroup.org/onlinepubs/009695399/functions/pthread_exit.ht
ml
Функция pthread_join
 int pthread_join(pthread_t thread,
void **value_ptr);
 Функция ожидания завершения потока
 Не определено поведение системы в случае, если поток завершён, но
для него не вызвана функция pthread_join – количество потоков
ограничено
 Параметр возвращает значение, переданное в функцию void
pthread_exit(void *value_ptr), вызываемую в контексте завершающегося
потока для его завершения
 Прототип функции описан в файле pthread.h
 Дополнительная информация
http://www.opengroup.org/onlinepubs/009695399/functions/pthread_join.ht
ml
Функция pthread_detach
 int pthread_detach(
pthread_t thread);
 Вызов этой функции для потока с указанным идентификатором thread
означает, что после его завершения его данные будут автоматически
уничтожены и ожидать его завершения вызовом функции pthread_join
будет нельзя
 Прототип функции описан в файле pthread.h
84
 Дополнительная информация
http://www.opengroup.org/onlinepubs/009695399/functions/pthread_detach.
html
Функция sched_yeld
 int sched_yield(void);
 Вызов этой функции приводит к освобождению текущим потоком
процессора и постановкой этого потока в конец очереди потоков с тем
же приоритетом
 Прототип функции описан в файле sched.h
 Дополнительная информация
http://www.opengroup.org/onlinepubs/009695399/functions/sched_yield.ht
ml
5.3 Функции работы со средствами синхронизации
Функция shmget
 int shmget(key_t key, size_t size,
int shmflg);
 Функция предназначена для создания или открытия области
разделяемой памяти
 Параметр shmflg содержит атрибуты
 Возвращаемое значение
o Идентификатор области разделяемой памяти, ассоциированный с
ключом key
o В случае ошибки -1 (код ошибки заносится в errno)
 Прототип функции описан в файле sys/shm.h
 Тип key_t описан в файле sys/ipc.h
 Дополнительная информация
http://www.opengroup.org/onlinepubs/009695399/functions/shmget.html
Функция shmat
 void *shmat(int shmid, const void *shmaddr,
int shmflg);
 Функция предназначена для получения указателя на область памяти
текущего процесса, ассоциированную с областью разделяемой памяти
 Варианты работы в зависимости от значения параметров shmaddr и
shmflg
o shmaddr == null, начальный адрес выделенной памяти
определяется ОС
o shmaddr != null && shmflg &SHM_RND != 0 начальный адрес
выделенной памяти определяется адресом, полученным в
85
результате вычисления выражения:
shmaddr - ((uintptr_t)shmaddr %SHMLBA))
o shmaddr != null && shmflg &SHM_RND == 0 начальный адрес
выделенной памяти определяется адресом, указанным в shmaddr
o Выделенная память доступна только для чтения если shmflg
&SHM_RDONLY != 0 и текущий процесс имеет права на чтение,
в остальных случаях выделенная память доступна для чтения и
записи
 Прототип функции описан в файле sys/shm.h
 Дополнительная информация
http://www.opengroup.org/onlinepubs/009695399/functions/shmat.html
Функция shmctl
 int shmctl(int shmid, int cmd,
struct shmid_ds *buf);
 Обеспечивает управление областью разделяемой памяти, включая её
удаление
 Варианты работы в зависимости от значения параметра cmd
o IPC_STAT в структуру buf заносится информация, актуальная
для области разделяемой памяти
o IPC_SET производится управление областью разделяемой памяти
в соответствии со значением структуры buf
o IPC_RMID область разделяемой памяти освобождается
 Возвращаемое значение
o В случае успеха 0
o В случае ошибки -1 (код ошибки заносится в errno)
 Прототип функции описан в файле sys/shm.h
 Дополнительная информация
http://www.opengroup.org/onlinepubs/009695399/functions/shmctl.html
Функция semget
 int semget(key_t key, int nsems, int semflg);
 Функция создаёт или открывает существующий семафор (группу
семафоров)
 Параметры
o nsems – количество создаваемых/открываемых семафоров
o semflg – флаги, права доступа
 Возвращаемое значение
o Неотрицательное целое число – идентификатор семафора,
ассоциированный с ключом key
o В случае ошибки -1 (код ошибки заносится в errno)
 Прототип функции описан в файле sys/sem.h
86
 Дополнительная информация
http://www.opengroup.org/onlinepubs/009695399/functions/semget.html
Функция semop
 int semop(int semid, struct sembuf *sops,
size_t nsops);
 Функция предназначена для изменения значения семафора
 Параметры
o semid – идентификатор семафора
o sops – массив структур
o nsops – количество структур nsops в массиве
 Структура sembuf
o sem_num – номер семафора
o sem_op – операция семафора
o sem_flg – флаги операции
 Прототип функции описан в файле sys/sem.h
 Дополнительная информация
http://www.opengroup.org/onlinepubs/009695399/functions/semop.html
Функция semctl
 int semctl(int semid, int semnum,
int cmd, ...);
 Функция предназначена для управления, установки и чтения значений,
инициализации и удаления семафора
 Параметры
o semid – идентификатор группы семафоров
o semnum – номер семафора в группе
o cmd – задаёт операцию с семафором
o arg – дополнительный параметр, имеет тип union semun, задаёт
дополнительные параметры операции
 Прототип функции описан в файле sys/sem.h
 Дополнительная информация
http://www.opengroup.org/onlinepubs/009695399/functions/semctl.html
Утилиты работы с объектами межпроцессоного взаимодействия
 ipcs (InterProcess Communication Status) – выводит информацию о
блоках разделяемой памяти, семафорах, очередях сообщений
o Дополнительная информация
http://www.opengroup.org/onlinepubs/009695399/utilities/ipcs.html
 ipcrm (InterProcess Communication ReMove) – удаляет блоки
разделяемой памяти, семафоры, очереди сообщений (это необходимо
при отладке, поскольку в случае некорректного завершения программ
указанные объекты остаются в памяти ОС)
87
o Дополнительная информация
http://www.opengroup.org/onlinepubs/009695399/utilities/ipcrm.html
Функция pipe
 int pipe(int fildes[2]);
 Позволяет организовать очередь сообщений без использования
функций вида msgxxx
o Отправка и приём сообщений производятся с помощью функций
работы с файлами через дескрипторы файлов
o Созданная очередь не имеет идентификатора
o Созданную очередь нельзя удалить явно, но очередь
автоматически удаляется после закрытия её дескрипторов из всех
процессов
 Дескриптор, хранящийся в fildes[1], предназначен для записи
сообщений в очередь
 Дескриптор, хранящийся в fildes[0], предназначен для чтения
сообщений из очереди
 Прототип функции описан в файле unistd.h
 Дополнительная информация
http://www.opengroup.org/onlinepubs/009695399/functions/pipe.html
Функция pthread_mutex_init
 int pthread_mutex_init(
pthread_mutex_t *restrict mutex,
const pthread_mutexattr_t
*restrict attr);
 Функция инициализации мьютекса с указателем mutex на основании
атрибутов, задаваемых параметром attr (значение NULL – атрибуты по
умолчанию)
 Прототип функции описан в файле pthread.h
 Дополнительная информация
http://www.opengroup.org/onlinepubs/009695399/functions/pthread_mutex
_init.html
Функция pthread_mutex_lock
 int pthread_mutex_lock(
pthread_mutex_t *mutex);
 Функция блокирующего ожидания захвата мьютекса,
приостанавливающая текущий поток до момента захвата мьютекса
 Параметр mutex задаёт указатель на мьютекс
 Прототип функции описан в файле pthread.h
88
 Дополнительная информация
http://www.opengroup.org/onlinepubs/009695399/functions/pthread_mutex
_lock.html
Функция pthread_mutex_trylock
 int pthread_mutex_trylock(
pthread_mutex_t *mutex);
 Функция неблокирующего ожидания захвата мьютекса, не
приостанавливающая текущий поток до момента захвата мьютекса
 Параметр mutex задаёт указатель на мьютекс
 Возвращает 0 в случае успеха
 Прототип функции описан в файле pthread.h
 Дополнительная информация
http://www.opengroup.org/onlinepubs/009695399/functions/pthread_mutex
_lock.html
Функция pthread_mutex_unlock
 int pthread_mutex_unlock(
pthread_mutex_t *mutex);
 Функция разблокировки мьютекса
 Прототип функции описан в файле pthread.h
 Дополнительная информация
http://www.opengroup.org/onlinepubs/009695399/functions/pthread_mutex
_unlock.html
Функция pthread_mutex_destroy
 int pthread_mutex_destroy(
pthread_mutex_t *mutex);
 Функция удаляет разблокированный мьютекс, который становится
неинициализированным и может быть использован повторно после
вызова функции pthread_mutex_init
 Прототип функции описан в файле pthread.h
 Дополнительная информация
http://www.opengroup.org/onlinepubs/009695399/functions/pthread_mutex
_destroy.html
Функция pthread_cond_init
 int pthread_cond_init(
pthread_cond_t *restrict cond,
const pthread_condattr_t
*restrict attr);
89
 Функция инициализации условной переменной с указателем cond на
основании атрибутов, задаваемых параметром attr (значение NULL –
атрибуты по умолчанию)
 Прототип функции описан в файле pthread.h
 Дополнительная информация
http://www.opengroup.org/onlinepubs/009695399/functions/pthread_cond_i
nit.html
Функция pthread_cond_signal
 int pthread_cond_signal(
pthread_cond_t *cond);
 Функция отправки события условной переменной, приводящего к
разблокированию хотя бы одного потока из числа потоков, ожидающих
событие этой условной переменной
 Прототип функции описан в файле pthread.h
 Дополнительная информация
http://www.opengroup.org/onlinepubs/009695399/functions/pthread_cond_s
ignal.html
Функция pthread_cond_broadcast
 int pthread_cond_broadcast(
pthread_cond_t *cond);
 Функция отправки события условной переменной, приводящего к
разблокированию всех потоков, ожидающих событие этой условной
переменной
 Прототип функции описан в файле pthread.h
 Дополнительная информация
http://www.opengroup.org/onlinepubs/009695399/functions/pthread_cond_b
roadcast.html
Функция pthread_cond_wait
 int pthread_cond_wait(
pthread_cond_t *restrict cond,
pthread_mutex_t *restrict mutex);
 Функция разблокирует мьютекс mutex и переводит текущий покток в
состояние ожидания условной переменной cond
 Операции разблокирования мьютекса и перевода потока в состояние
ожидания осуществляются атомарно
 Прототип функции описан в файле pthread.h
90
 Дополнительная информация
http://www.opengroup.org/onlinepubs/009695399/functions/pthread_cond_
wait.html
Функция pthread_cond_destroy
 int pthread_cond_destroy(
pthread_cond_t *cond);
 Функция уничтожения условной переменной cond, которая может быть
использована повторно в вызове функции pthread_cond_init
 Прототип функции описан в файле pthread.h
 Дополнительная информация
http://www.opengroup.org/onlinepubs/009695399/functions/pthread_cond_d
estroy.html
91
6
Системы программирования на основе передачи сообщений
6.1 Основы MPI
MPI (Message Passing Interface – интерфейс передачи сообщений) –
программный интерфейс (библиотека, API), предоставляющий процессам,
выполняющим одну задачу, возможность обмена сообщениями
 Разработан группой MPI Forum http://www.mpi-forum.org, поддержан
академическими кругами (университет Иллинойса) и
промышленностью
 Является одним из первых стандартов систем передачи сообщений
 Применяется в системах с распределённой памятью, кластерах
 Основные особенности
o Высокая производительность
o Масштабируемость
o Переносимость (портируемость)
 Для упрощения переносимости используется языково-независимая
спецификация (Language-independent specification – LIS)
Стандарты MPI
 MPI-1 (≈128 функций)
o Передача и получение сообщений между отдельными процессами
(отправка сообщений указанному процессу)
o Коллективные взаимодействия процессов (отправка сообщений
нескольким получателям)
o Взаимодействия между процессами в группах процессов
(сообщения, синхронизация)
o Виртуальные топологии процессоров
 MPI-2 (≈500 функций)
o Включает MPI-1
o Динамическое порождение процессов и управление процессами
(процессы допустимо создавать в процессе работы программы)
o Односторонние коммуникации (обмены Get/Put)
o Поддержка операций параллельного файлового ввода и вывода
o Расширенные коллективные операции (процессы могут
выполнять коллективные операции не только внутри одного
коммуникатора, но и в рамках нескольких коммуникаторов)
Общая структура MPI-программы
 Подключение необходимых библиотек (заголовочных файлов,
областей имён)
 Инициализация
92
o MPI_Init
o Параметры – аргументы командной строки
o До инициализации не рекомендуется изменять состояние
процесса, например, осуществлять ввод/вывод
 Использование
 Деинициализация
o MPI_Finalize
o Перед денициализацией все обмены должны быть завершены –
это забота программиста
o После деинициализации количество работающих процессов не
определено – лучше завершить работу программы
o Заново инициализировать MPI нельзя
Процессы в MPI
 Для запуска приложений MPI используется специальная утилита
(mpiexec)
 Для обеспечения функционирования MPI приложений на
распределённых архитектурах используются службы (демоны) MPI
 Каждый MPI-процесс имеет свой номер – ранг процесса, используемый
для его идентификации
 Первый запущенный процесс имеет ранг 0
6.1.1 Сообщения
Виды сообщений по количеству взаимодействующих процессов
 Попарные (point-to-point, двухточечные) – сообщение отправляется от
одного процесса к другому
o Используется для неструктурированных и локальных
взаимодействий
 Коллективные – сообщения отправляется и/или принимаются
несколькими процессами
o Эффективней множества попарных сообщений за счёт учёта
архитектуры вычислительной системы
Виды сообщений по типам вызовов
 Синхронные – процесс-отправитель сообщения ожидает готовности
процесса-получателя принять сообщение
 Асинхронные – процесс-отправитель сообщения не ждёт готовности
процесса-получателя, доставка сообщения обеспечивается внутренним
буфером MPI
93
 Первые реализации MPI предусматривали только синхронные
сообщения
 Последние реализации MPI используют асинхронные сообщения для
попарных обменов
Основные составляющие сообщения
 Блок данных
 Метаинформация блока данных
o Тип данных – в MPI есть собственные типы данных, являющиеся
реализацией канонических типов данных
o Размер данных
 Информация об отправителе и получателе сообщения
o Коммуникатор (группа) – рассматривается в следующей части
o Ранг отправителя
o Ранг получателя
 Тег сообщения – целое число от 0 до 32767
o Используется для фильтрации сообщений получателем или
определения порядка следования сообщений
o Отсутствует в коллективных обменах сообщениями
Виды коллективных сообщений
 Широковещательная передача – передача сообщения от одного
процесса сразу всем процессам
 Распределение данных – отправка сообщений от одного процесса
нескольким процессам
 Сбор данных – получение сообщений одним процессом от нескольких
процессов
 Обмен с барьером – ожидание заданного количества процессов
 Операции приведения – разновидность сбора данных, при которой
полученные данные становятся доступными всем процессам, из
которых осуществлялся сбор данных
 Операции сканирования – разновидность операции приведения, после
выполнения которой данные процессов не идентичны, а
последовательно передаются между процессами, как при конвейерной
обработке
6.1.2 Коммуникаторы
 Процессы разделяются на виртуальные группы, внутри которых
осуществляются обмены сообщениями
 Коммуникатор – идентификатор группы взаимодействующих
процессов, имеющих возможность обмениваться сообщениями
94
 Один и тот же процесс может одновременно входить в несколько
коммуникаторов
 В каждом коммуникаторе процесс будет иметь собственный ранг
Коммуникаторы по умолчанию
 MPI_COMM_WORLD – содержит все процессы
o Первый запущенный процесс именно в этом коммуникаторе
будет иметь ранг 0
 MPI_COMM_SELF – содержит только процесс, в контексте которого
вызвана функция
 MPI_COMM_NULL – пустой коммуникатор, не содержит ни одного
процесса
Назначение коммуникаторов
 Возможность организации коллективных обменов сообщениями
внутри групп процессов
 Изоляция обменов различного назначения
 Учёт архитектуры вычислительной системы (кластера) для повышения
скорости обмена сообщениями
6.1.3 Обмен сообщениями
Функции отправки и приёма сообщений
 Попарные сообщения
o MPI_Send – блокирующая отправка
o MPI_Recv – блокирующий приём
o MPI_Isend – неблокирующая отправка
o MPI_Irecv – неблокирующий приём
 Коллективные сообщения
o MPI_Barrier – синхронизация всех процессов в коммуникаторе
o MPI_Bcast – отправка сообщений всем процессам в
коммуникаторе
o MPI_Reduce – получение сообщений от нескольких процессов в
коммуникаторе
o MPI_Abort – запрос на завершение всех процессов
Имитация «вызова» функции
 Порядок выполнения операций
o Отправка сообщения со входными параметрами из вызывающего
процесса
o Приём сообщения в вызываемом процессе
o Обработка информации
95
o Отправка сообщения с результатом выполнения функции в
вызывающий процесс
o Приём сообщения в вызывающем процессе
 Недостатки
o Большое количество кода
o Невысокое быстродействие
o Трудность отладки, особенно при переходе от синхронных
сообщений к асинхронным
Попарный обмен сообщениями
 Предусмотрена функция обмена сообщениями, обеспечивающая
имитацию «вызова», MPI_Sendrecv
o Эта функция принимает сообщения от MPI_Send
o Сообщения этой функции принимаются с помощью MPI_Recv
 Функция MPI_Sendrecv_Replace позволяет использовать один буфер
данных для отправки и получения сообщений
Ввод и вывод информации
 Есть реализации MPI, в которых всем процессам разрешается выводить
информацию на консоль
o Данные «перемешиваются»
 Есть реализации MPI, в которых на консоль разрешается выводить
информацию только процессу с рангом 0 в коммуникаторе
MPI_COMM_WORLD
 Есть реализации MPI, в которых ввод/вывод осуществляется только
посредством файлов
o Наиболее характерно для распределённых систем
o Рекомендуется каждому процессу назначать собственный файл
для ввода/вывода
Реализации MPI
 MPICH – MPI Chameleon – одна из первых реализаций MPI
(http://www.mcs.anl.gov/research/projects/mpich2/)
o Бесплатна
o Легко переносится на различные платформы
o Работает на Unix, Linux и Windows NT
 IntelMPI – коммерческая реализация MPI от Intel для Linux и Windows
NT (http://software.intel.com/en-us/articles/intel-mpi-library/)
 Open MPI – бесплатная реализация MPI с открытым кодом
(http://www.open-mpi.org)
96
 mpiJava – реализация MPI для Java в рамках проекта HPJava
(http://www.hpjava.org/mpiJava.html)
 Microsoft MPI – реализация MPI от Microsoft в составе HPC Server
(http://blogs.msdn.com/b/ru-hpc/archive/2009/12/28/ms-mpi-visual-studiowindows-hpc-server.aspx)
 MPI.NET – бесплатная реализация MPI для .NET, выполненная
университетом Индинаны (http://www.osl.iu.edu/research/mpi.net/)
 Дополнительная информация
http://ru.wikipedia.org/wiki/Message_Passing_Interface#.D0.A0.D0.B5.D0.
B0.D0.BB.D0.B8.D0.B7.D0.B0.D1.86.D0.B8.D0.B8_MPI
97
7
Новые средства многопоточного программирования
Недостатки традиционной реализации многопоточности
 Традиционные средства императивных языков программирования
рассчитаны на однопоточное выполнение инструкций
 Расширение возможностей языков программирования по поддержке
многопоточности обеспечивается за счёт применения готовых
библиотек и API операционных систем
o Предоставляются низкоуровневые примитивы, ориентированные
на реализацию потоков и их синхронизацию, но не
распараллеливание обычных алгоритмов
o Реализация и отладка даже несложных алгоритмов в
многопоточных программах становится трудной задачей
o Требуется значительное время на обеспечение приемлемого
качества кода
o Необходимо самостоятельно управлять количеством потоков с
учётом аппаратных возможностей
o Разработанный код трудно обслуживать и использовать повторно
Современные тенденции развития поддержки многопоточности
 Добавление структур данных, обеспечивающих параллельную
обработку
 Разделение алгоритма на отдельные параллельно выполняющиеся
задачи, автоматически распределяемые между процессорами
 Использование алгоритмов распараллеливания, предусматривающих
разделение задачи на множество небольших частей (мелкозернистый
параллелизм)
 Дополнительная информация
http://www.ibm.com/developerworks/ru/library/j-jtp11137/
Развитие поддержки многопоточности в .NET
 Библиотека Parallel Extensions
o Входит в состав .NET 4
o Параллелизм при декларативной обработке данных
 Параллельный интегрированный язык запросов (PLINQ) –
параллельная реализация LINQ
o Параллелизм при императивной обработке данных
 Параллельные варианты основных итеративных операций
над структурами данных (циклы for и foreach)
o Параллелизм на уровне задач
98
 Высокоуровневая работа с пулом рабочих потоков
 Использование легковесных задач
 Встроенный в библиотеку планировщик задач обеспечивает
диспетчеризацию и управление исполнением легковесных
задач с учётом доступных вычислительных ядер, и
единообразный механизм обработки исключений
 Механизм работы пула потоков и планировщика задач был
переработан
 Дополнительная информация http://blogs.msdn.com/b/pfxteam/
http://msdn.microsoft.com/en-us/library/dd460693.aspx
Состав Parallel Extensions
 TPL (Task Parallel Library)
o Parallel.For, Parallel.ForEach, Parallel.Invoke
 PLINQ (Parallel Language-Integrated Query)
o LINQ to Objects, LINQ to XML
o AsParallel()
 CDS (Coordination Data Structures)
o Типы структур данных, используемых для синхронизации и
координации выполнения параллельных задач
Особенности Parallel Extensions
 Использование задач (Task)
o Задача – это небольших участок кода, реализованный в виде
лямбда-функции, который может исполняться независимо от
остальных задач
o TPL предусматривает разбиение блоков кода или итераций
циклов на задачи
o Задачи могут исполняться параллельно, при использовании
общих ресурсов задачи должны обеспечивать синхронизацию
 Использование специального планировщика задач
o Он обеспечивает распределение задач по рабочим потокам
o Количество рабочих потоков по умолчанию равно количеству
вычислительных ядер
o Каждый рабочий поток имеет собственную очередь задач,
ожидающих исполнения
o Каждый процессор имеет собственный пул потоков
o Каждый процессор может заимствовать задачи из других
процессоров
Состав Task Parallel Library (TPL)
99
 System.Threading.Tasks.Task http://msdn.microsoft.com/enus/library/system.threading.tasks.task.aspx
o Класс, представляющий собой задачу, не возвращающую
значение
 System.Threading.Tasks.Task<TResult> http://msdn.microsoft.com/enus/library/dd321424.aspx
o Класс, представляющий собой задачу, имеющую возвращаемое
значение
 System.Threading.Tasks.Parallel http://msdn.microsoft.com/enus/library/system.threading.tasks.parallel.aspx
o Предназначен для распараллеливания циклов и
последовательностей блоков кода за счёт использования задач
o Имеет набор статических методов For, ForEach и Invoke
o Parallel.For и Parallel.ForEach корректно использовать если ни в
одной итерации цикла не используется результаты работы
предыдущих итераций, например, изменённое значение
предыдущей ячейки массива
o Допустимо использовать Parallel.For и Parallel.ForEach в случае
когда между итерациями передаётся общая информация,
например, при расчёте суммы элементов массива
o Parallel.Invoke позволяет распараллелить исполнение
независимых блоков операторов, например, для рекурсивных
алгоритмов
 System.Threading.Tasks.TaskScheduler http://msdn.microsoft.com/enus/library/system.threading.tasks.taskscheduler.aspx
o Класс, позволяющий управлять экземпляром очереди
выполнения задач за счёт постановки задачи в очередь,
извлечения задачи из очереди и т.п.
Пример использования класса Task
Task taskExample = new Task(() =>
{
for (int i = 0; i < 10; i++)
{
Console.WriteLine("Task loop iteration: {0}.",
i);
}
throw new ArgumentException("This is sample
exception.");
});
taskExample.Start();
for (int i = 0; i < 10; i++)
{
100
Console.WriteLine("Main thread loop iteration:
{0}.", i);
}
try
{
taskExample.Wait();
}
catch (AggregateException e)
{
Console.WriteLine("Exception occured: {0} {1}",
e.GetType(), e.Message);
foreach (var ie in e.InnerExceptions)
{
Console.WriteLine("Inner exception: {0} {1}",
ie.GetType(), ie.Message);
}
}
Пример использования класса Task<Result>
Task<int> taskExample = new Task<int>(() =>
{
int j = 0;
for (int i = 0; i < 10; i++)
{
Console.WriteLine("Task loop iteration: {0}.",
i);
j += 1 << i;
}
return j;
});
taskExample.Start();
// Some code
taskExample.Wait();
Console.WriteLine("Task result: {0}",
taskExample.Result);
Создание параллельного цикла на основе Parallel.For
 Обычный цикл (однопоточный)
int[] arraySample = { 1, 2, 3, 4, 5 };
for (int i = 0; i < arraySample.Length; i++)
{
arraySample[i] += 1;
Console.WriteLine("arraySample[{0}]: {1}",
101
i, arraySample[i]);
}
 Параллельный цикл на основе Parallel.For
o Из-за одновременного запуска задач в разных потоках порядок
вывода на консоль информации об элементах массива может
отличаться от традиционного: 0 1 2 3 4, и быть, например: 0 2 4 1
3
int[] arraySample = { 1, 2, 3, 4, 5 };
Parallel.For(0, arraySample.Length, i =>
{
arraySample[i] += 1;
Console.WriteLine("arraySample[{0}]: {1}",
i, arraySample[i]);
});
Дополнения области имён System.Threading
 System.Threading.CountdownEvent http://msdn.microsoft.com/enus/library/system.threading.countdownevent.aspx
o Вызывает событие после указанного количества вызовов метода
Decrement
 System.Threading.LazyInitializer http://msdn.microsoft.com/enus/library/system.threading.lazyinitializer.aspx
o Позволяет выполнять потокобезопасную ленивую
инициализацию
Состав Coordination Data Structures (CDS)
 System.Collections.Concurrent.ConcurrentStack<T>
http://msdn.microsoft.com/en-us/library/dd267331.aspx
o Потокобезопасный вариант стека LIFO
 System.Collections.Concurrent.ConcurrentQueue<T>
http://msdn.microsoft.com/en-us/library/dd267265.aspx
o Потокобезопасный вариант очереди FIFO
 System.Collections.Concurrent.BlockingCollection<T>
http://msdn.microsoft.com/en-us/library/dd381957.aspx
o Потокобезопасный вариант структуры данных, реализующей
шаблон «издатель-потребитель»: издатель формирует данные,
потребитель их по очереди обрабатывает
 System.Collections.Concurrent.ConcurrentDictionary<TKey, TValue>
http://msdn.microsoft.com/en-us/library/dd287191.aspx
o Потокобезопасный вариант структуры коллекции «ключзначение»
102
 System.Collections.Concurrent.ConcurrentBag<T>
http://msdn.microsoft.com/en-us/library/dd381779.aspx
o Потокобезопасный вариант несортированного динамического
массива объектов
 System.Collections.Concurrent.Partitioner http://msdn.microsoft.com/enus/library/system.collections.concurrent.partitioner.aspx
 System.Collections.Concurrent.Partitioner<TSource>
http://msdn.microsoft.com/en-us/library/dd381768.aspx
 System.Collections.Concurrent.OrderablePartitioner<TSource>
http://msdn.microsoft.com/en-us/library/dd394988.aspx
o Классы, предназначенные для разделения структур данных
(массивов, списков) на разделы (partitions), позволяющие
осуществить балансировку вычислительной нагрузки при их
обработке
Использование PLINQ
 Основан на классе System.Linq.ParallelEnumerable
http://msdn.microsoft.com/enus/library/system.linq.parallelenumerable.aspx
 Основные методы расширения класса ParallelEnumerable
o AsParallel http://msdn.microsoft.com/enus/library/system.linq.parallelenumerable.asparallel.aspx
 Включает распараллеливание выполнения запроса (по
возможности)
o AsSequential<TSource> http://msdn.microsoft.com/enus/library/dd383949.aspx
 Отключает распараллеливание для запроса
 При возможности распараллеливания выполнение частей запроса
осуществляется посредством задач (класс Task)
 Дополнительная информация http://msdn.microsoft.com/enus/library/dd997425.aspx
Пример использования PLINQ
Обычная выборка данных (однопоточная)
int[] arraySample = { 1, 2, 3, 4, 5 };
string[] strings =
(from i in arraySample
where i > 2
select i.ToString()).ToArray();
Выборка данных при использовании PLINQ
int[] arraySample = { 1, 2, 3, 4, 5 };
103
string[] strings =
(from i in arraySample.AsParallel()
where i > 2
select i.ToString()).ToArray();
7.1 Совершенствование пулов потоков
Преимущества использования пула потоков
 Возможность одновременно обрабатывать ограниченное количество
запросов клиентов с постановкой в очередь ожидающих запросов
 Эффективно при наличии значительного числа операций ввода-вывода
и ожидания внешних сервисов
 Повышение эффективности использования аппаратных ресурсов
системы
 Возможность быстро запускать обработку поступившего запроса при
наличии одного или нескольких свободных потоков в пуле
Изменения в работе пула потоков в .NET 4
 Причины
o Введение задач (класс Task), не порождающих отдельных
потоков, а распределяемых между потоками в пуле
o Необходимость диспетчеризации и управления задачами, а не
потоками
o Необходимость балансировки вычислительной нагрузки в
многоядерных системах
 Сделанные изменения
o Каждому потоку из пула потоков назначается локальная очередь,
содержащая задачи, ожидающие исполнения в этом потоке
o Добавлен механизм заимствования задач между локальными
очередями потоков, по правилу LIFO
7.2 Транзакционная память
 Транзакционная память – модель работы с памятью
 Обеспечивает механизм легковесных транзакций для потоков
управления, выполняемых в общем адресном пространстве, при
обращении к памяти
 Гарантирует атомарность и изолированность параллельно
выполняемых задач
 Альтернатива использованию дорогостоящих блокировок при работе с
данными
 В основе системы лежат два механизма
o Управление версиями данных (data versioning)
104
o Обнаружение конфликтов (conflict detection)
Семантика
 Для каждой транзакции система исполнения обеспечивает ACIDсвойства
 Если транзакция противоречит некоторой транзакции,
зафиксированной в процессе своего выполнения (то есть, читает или
пишет измененный элемент данных), то транзакция откатывается –
результаты её действий отменяются, и она может быть перезапущена
(чаще всего системы STM автоматически перезапускают транзакцию в
случае конфликтов)
 Если необработанное исключение покидает пределы транзакции, то
транзакция откатывается и не перезапускается
 Вложенные транзакции фиксируются вместе с родительской
транзакцией (то есть, относительно фиксации дерево транзакций
выпрямляется в список), но откатываются по отдельности (например,
исключение, выброшенное вложенной транзакцией, но обработанное в
родительской, отменит только вложенную)
Управление версиями данных
 Каждой транзакции система выделяет приватную область памяти,
недоступную другим транзакциями, в которой транзакция хранит
результаты своей работы
 Существуют две основные реализации области памяти
o Работа осуществляется с копией объекта и результат становится
виден только при завершении транзакции
o Работа осуществляется непосредственно с объектом данных, при
этом создаются копии каждой изменённой ячейки памяти
Обнаружение конфликтов
 Коллизии возникают в тех случаях, когда две или более транзакции,
выполняемые параллельно, обращаются к одному и тому же фрагменту
данных в тот момент, когда одна из них записывает новую версию этих
данных
 Средствами обнаружения и разрешения конфликтов обеспечивается
комплекс характеристик ACID, эти средства включают в себя два
набора адресов, соответствующих каждой транзакции; в один
записываются адреса, откуда она читает, во второй — адреса, куда она
пишет
 При разрешении коллизий этот механизм может действовать по одному
из двух сценариев
o Пессимистический сценарий – предполагает, что конфликтные
ситуации возникают непосредственно в процессе выполнения
транзакций, они обнаруживаются на ранних моментах и могут
105
быть разрешены путем приостановления одной из транзакций
или прерыванием одной из транзакций с тем, чтобы возобновить
ее позднее
o Оптимистический сценарий – исходит из того, что вероятность
бесконфликтного завершения транзакций велика, поэтому
текущей транзакции позволяют завершиться до конца, а затем
только производится проверка
 Обнаружение конфликтов возможно на уровне объектов данных либо
на уровне отдельных слоёв (участки памяти, поля объекта)
Реализации
 Существует два способа реализации:
o Программная транзакционная память (STM)
o Специальная аппаратная поддержка (HTM)
Программная транзакционная память
 Альтернатива традиционным способам блокировок используемым в
многопоточно среде
 Реализуется за счет запуска сложной системы исполнения, работа
системы исполнения схожа с работой системы сборки мусора
 Доступна при использовании сторонних библиотек или расширений
языка и/или виртуальной машины
Пример использования STM для .NET
 Проверка целостности справочника
bool ValidateMapping(string name, string phone)
{
string outPhone = null;
string outName = null;
atomic
{
outPhone = name2phone.TryGetValue(name);
outName = phone2name.TryGetValue(phone);
}
// Код проверки соответствия
// номера телефона и абонента
}
Пример использования STM для Java
106
Библиотеки STM
 Java
o Multiverse (http://multiverse.codehaus.org)
o Deuce (http://www.deucestm.org/)
o SCAT (http://wasp.cs.washington.edu/wasp_scat.html)
 .NET
o NSTM (http://code.google.com/p/nstm/)
o STM.NET (http://msdn.microsoft.com/en-us/devlabs/ee334183.aspx)
o LibCMT (http://sourceforge.NET/projects/libcmt)
Аппаратная транзакционная память
 Позволяет перенести частично или полностью функции, связанные
с управлением версиями данных и предупреждением возникающих
конфликтов, которые выполняет STM, на аппаратный уровень
 В основе аппаратной поддержки – иерархия кэш-памяти (cache
hierarchy) и протокол согласования кэш-памяти (cache coherence
protocol); они обеспечивают управление версиями данных
и обнаружение конфликтных ситуаций
Подробности реализации
 Для отслеживания наборов прочитанных и измененных объектов
каждый блок кэша помечается служебными битами R и W, которые
устанавливаются при первом доступе к блоку внутри транзакции по
чтению или записи соответственно
107
 Блоки кэша, входящие в набор измененных объектов, действуют как
буфера записи и не выталкиваются в память до фиксации транзакции
 Выявление конфликтов происходит, когда другие процессоры
получают сообщения о согласовании кэшей от фиксируемой
транзакции
Минусы использования транзакционной памяти
 Многие аспекты семантики и реализации TM все еще остаются
предметом активных исследований
 Проблемы с производительностью при малых масштабах вычислений
 Проблемы с откатом транзакций для операций ввода-вывода
Перспективы развития
 Система транзакций хорошо зарекомендовала себя при работе в
многопоточном окружении в базах данных
 Транзакционная модель работы с памятью повышает уровень
абстракции для понимания параллельных задач и помогает избежать
многочисленных коварных ошибок параллельного программирования
108
109
Приложения
Пример расчёта чисел Фибоначчи пример программы на C#.
/// <summary>
/// Represents Fibonacci number calculation routines.
/// </summary>
public class Fibonacci
{
private int stepsNumber;
private int result;
private ManualResetEvent calculationComplete;
/// <summary>
/// Gets this Fibonacci calculation argument.
/// </summary>
public int StepsNumber { get { return stepsNumber;
} }
/// <summary>
/// Gets this Fibonacci calculation result.
/// </summary>
public int Result { get { return result; } }
/// <summary>
/// Creates new Fibonacci number calculation.
/// </summary>
/// <param name="stepsNumber">Fibonacci calculation
/// argument.</param>
/// <param name="calculationComplete">Event,
/// signaling calculation complete.</param>
public Fibonacci(int stepsNumber,
ManualResetEvent calculationComplete)
{
this.stepsNumber = stepsNumber;
this.calculationComplete = calculationComplete;
}
/// <summary>
/// Starts asynchronous calculation in thread pool.
/// </summary>
/// <param name="threadContext">Paramenter
/// passing from thread pool.</param>
public void ThreadPoolCallback(object threadContext)
{
int threadIndex = (int)threadContext;
Debug.Print("Thread {0} started...", threadIndex);
result = Calculate(stepsNumber);
110
Debug.Print("Thread {0} result calculated...",
threadIndex);
calculationComplete.Set();
}
/// <summary>
/// Synchronously calculates Fibonacci number
/// for specified argument.
/// </summary>
/// <param name="stepsNumber">Fibonacci
/// number argument.</param>
/// <returns>Fibonacci number result.</returns>
public static int Calculate(int stepsNumber)
{
if (stepsNumber <= 1)
{
return stepsNumber;
}
return Calculate(stepsNumber - 1) +
Calculate(stepsNumber - 2);
}
}
/// <summary>
/// Contains program entry point routines.
/// </summary>
public class Program
{
private const int numberOfCalculations = 64;
private static ManualResetEvent[] doneEvents =
new ManualResetEvent[numberOfCalculations];
private static int[] arguments =
CreateArguments();
private static int[] results =
new int[numberOfCalculations];
private static Fibonacci[] calculations =
new Fibonacci[numberOfCalculations];
/// <summary>
/// Program entry point.
/// </summary>
public static void Main()
{
TimeSpan elapsedTime;
Console.WriteLine("Launching {0} synchronous
tasks...",
numberOfCalculations);
elapsedTime = CalculateSynchronous();
111
Console.WriteLine("All synchronous calculations are
complete.");
PrintSynchronousResults(elapsedTime);
Console.WriteLine("Launching {0} asynchronous
tasks...",
numberOfCalculations);
elapsedTime = CalculateAsynchronous();
Console.WriteLine("All asynchronous calculations
are complete.");
PrintAsynchronousResults(elapsedTime);
}
private static int[] CreateArguments()
{
Random random = new Random();
int[] arguments = new int[numberOfCalculations];
for (int i = 0; i < numberOfCalculations; i++)
{
arguments[i] = random.Next(20, 40);
}
return arguments;
}
private static TimeSpan CalculateSynchronous()
{
DateTime dateStart = DateTime.Now;
for (int i = 0; i < numberOfCalculations; i++)
{
results[i] = Fibonacci.Calculate(arguments[i]);
}
DateTime dateFinish = DateTime.Now;
return dateFinish - dateStart;
}
private static TimeSpan CalculateAsynchronous()
{
DateTime dateStart = DateTime.Now;
for (int i = 0; i < numberOfCalculations; i++)
{
doneEvents[i] = new ManualResetEvent(false);
Fibonacci f = new Fibonacci(arguments[i],
doneEvents[i]);
calculations[i] = f;
ThreadPool.QueueUserWorkItem(f.ThreadPoolCallback, i);
}
WaitHandle.WaitAll(doneEvents);
DateTime dateFinish = DateTime.Now;
112
return dateFinish - dateStart;
}
private static void PrintAsynchronousResults(
TimeSpan elapsedTime)
{
foreach (Fibonacci f in calculations)
{
PrintResult(f.StepsNumber, f.Result);
}
PrintElapsedTime(elapsedTime);
}
private static void PrintSynchronousResults(
TimeSpan elapsedTime)
{
for (int i = 0; i < numberOfCalculations; i++)
{
PrintResult(arguments[i], results[i]);
}
PrintElapsedTime(elapsedTime);
}
private static void PrintResult(
int argument, int result)
{
Console.WriteLine("Fibonacci({0}) = {1}",
argument, result);
}
private static void PrintElapsedTime(
TimeSpan elapsedTime)
{
Console.WriteLine("Elapsed time: {0}",
elapsedTime);
}
}
Пример перемножения матриц на Java.
package ru.supercorporation;
import java.util.Arrays;
import java.util.List;
import java.util.Vector;
import java.util.concurrent.*;
113
import java.util.concurrent.atomic.AtomicInteger;
/**
* Math matrix class.
*/
public class Matrix {
protected final int[][] data;
/**
* Get matrix table. [row][column]
*
* @return matrix table
*/
public int[][] getData() {
return data;
}
/**
* @param rawData matrix table
*/
public Matrix(int[][] rawData) {
data = rawData;
}
/**
* Matrix factory.
*
* @param rows
rows
* @param columns
cols
* @param lessThen fills with random numbers
0<=x<lessThen
* @return matrix
114
*/
public static Matrix createRandomMatrix(int rows, int
columns, int lessThen) {
int[][] raw = new int[rows][columns];
for (int i = 0; i < rows; i++) {
for (int j = 0; j < columns; j++) {
raw[i][j] = (int) (Math.random() *
lessThen);
}
}
return new Matrix(raw);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
} else if (obj instanceof Matrix) {
int[][] data2 = ((Matrix) obj).getData();
for (int i = 0; i < data.length; i++) {
for (int j = 0; j < data2[i].length; j++) {
if (data[i][j] != data2[i][j]) {
return false;
}
}
}
return true;
} else {
return false;
115
}
}
/**
* @return rows
*/
public int getHeight() {
return data.length;
}
/**
* @return columns
*/
public int getWidth() {
return data[0].length;
}
@Override
public String toString() {
return Arrays.deepToString(data);
}
/**
* @param a
first matrix raw data
* @param b
second matrix raw data
* @param result resulting matrix
* @param row row number to multiply
*/
private void multiplySingleRow(
int[][] a, int[][] b, int[][] result,
int row) {
int cols = getWidth();
116
int rows = getHeight();
for (int j = 0; j < cols; ++j) {
result[row][j] += a[row][j] *
b[j][row];
}
}
/**
* Multiply by another matrix in single thread.
*
* @param matrix second matrix
* @return result of multiplication
*/
public Matrix multiply(Matrix matrix) {
int cols = getWidth();
int rows = getHeight();
int[][] result = new int[rows][cols];
for (int i = 0; i < rows; ++i) {
multiplySingleRow(data, matrix.data,
result, i);
}
return new Matrix(result);
}
/**
* Multiply by another matrix with help of {@link
java.util.concurrent.ExecutorService}.
*
* @param matrix
second matrix
117
* @param threadsNumber number of simultaneous
threads
* @return result of multiplication
*/
public Matrix multiplyExecutorService(final Matrix
matrix, int threadsNumber)
throws InterruptedException, ExecutionException
{
final int[][] result = new
int[getHeight()][getWidth()];
ExecutorService es =
Executors.newFixedThreadPool(threadsNumber);
Vector<Callable<Object>> tasks = new
Vector<Callable<Object>>(data.length);
// task per row
for (int i = 0; i < getHeight(); ++i) {
final int row = i;
tasks.add(new Callable<Object>() {
public Object call() throws Exception {
multiplySingleRow(data, matrix.data,
result, row);
return null;
}
});
}
List<Future<Object>> futures = es.invokeAll(tasks);
// wait for complete
for (Future<Object> future : futures) {
future.get();
}
118
es.shutdown();
return new Matrix(result);
}
/**
* Multiply by another matrix. Multithreaded.
*
* @param matrix
second matrix
* @param threadsNumber number of simultaneous
threads
* @return result of multiplication
*/
public Matrix multiplyLockFreeThreads(final Matrix
matrix, int threadsNumber)
throws InterruptedException {
final int[][] result = new
int[getHeight()][getWidth()];
final int rows = getHeight();
Thread[] threads = new Thread[threadsNumber];
for (int i = 0; i < threadsNumber; i++) {
final int start = i * (rows /
threadsNumber);
final int before = i != threadsNumber - 1 ?
(i + 1) *
(rows / threadsNumber) : rows;
threads[i] = new Thread(new Runnable() {
public void run() {
for (int i = start; i < before;
++i) {
multiplySingleRow(data,
matrix.data, result, i);
119
}
}
});
threads[i].start();
}
// wait for complete
for (Thread thread : threads) {
thread.join();
}
return new Matrix(result);
}
}
package ru.supercorporation;
import java.util.concurrent.ExecutionException;
public class MultiplyMain {
public static void main(String[] args) {
System.out.println("Starting Matrix multiply");
Matrix a = Matrix.createRandomMatrix(8000,
1000, 10);
Matrix b = Matrix.createRandomMatrix(1000,
8000, 10);
benchmarkMultiplication(a, b, 4);
System.out.println("Completed");
}
public static void benchmarkMultiplication(Matrix a,
Matrix b, int threadsNumber) {
long time = System.currentTimeMillis();
Matrix result1 = a.multiply(b);
120
in
System.out.println("Computed in single thread. Done
"
+ (System.currentTimeMillis() - time));
time = System.currentTimeMillis();
try {
Matrix result2 = a.multiplyExecutorService(b,
threadsNumber);
System.out.println("Computed using
ExecutorService with " +
threadsNumber + " threads. Done in "
+ (System.currentTimeMillis() - time));
assert result1.equals(result2) : "answers not
equals";
} catch (InterruptedException ie) {
assert false : ie;
} catch (ExecutionException e) {
assert false : e;
}
time = System.currentTimeMillis();
try {
Matrix result3 = a.multiplySimpleThreads(
b, threadsNumber);
System.out.println(
"Computed using simple Threads with "
+
threadsNumber + " threads. Done in " +
(System.currentTimeMillis() - time));
assert result1.equals(result3) : "answers
not equals";
} catch (InterruptedException ie) {
121
assert false : ie;
}
time = System.currentTimeMillis();
try {
Matrix result4 = a.multiplySimpleThreads(
b, threadsNumber);
System.out.println(
"Computed using lock free Threads with
" +
threadsNumber + " threads. Done in " +
(System.currentTimeMillis() - time));
assert result1.equals(result4) : "answers
not equals";
} catch (InterruptedException ie) {
assert false : ie;
}
}
}
122
Пример создания процесса в Java
ProcessBuilder pb =
new ProcessBuilder(
"myCommand",
"myArg1",
"myArg2");
Process p = pb.start();
123
Download