Параллельное и распределенное программирование на С++
Шрифт:
Существует ряд моделей, которые можно использовать для делегирования работы потокам и управления их созданием и аннулированием. В модели делегирования один поток (управляющий) создает другие потоки (рабочие) и назначает им задачи. Управляющий поток ожидает до тех пор, пока каждый рабочий поток не завершит свою задачу. При использовании модели равноправных узлов есть один поток, который изначально создает все потоки, необходимые для выполнения всех задач, причем этот поток считается рабочим потоком, поскольку он не осуществляет никакого делегирования. Все потоки в этой модели имеют одинаковый статус. Применяя модель конвейера, программу можно охарактеризовать как сборочную линию, в которой входной поток (поток входных данных) обрабатывается поэтапно. На каждом этапе поток обрабатывает некоторую порцию входных элементов. Порция входных элементов перемещается от одного потока выполнения к следующему до тех пор, пока не завершится вся предусмотренная обработка. На последнем этапе работы конвейера формируются его результаты, т.е. последний поток отвечает за формирование конечных результатов программы.
Для создания и управления потоками многопоточного приложения можно использовать библиотеку Pthread. Библиотека Pthread опирается на стандартизированный программный интерфейс, предназначенный для создания и поддержки потоков Этот интерфейс определен комитетом стандартов IEEE в стандарте POSIX 1003.1с Сторонние производители при создании своих продуктов должны придерживаться этого стандарта POSIX.
Синхронизация параллельно выполняемых задач
Отношение этих механизмов ко времени требует тщательного изучения. <...> Нас почти не интересовала производительность вычислительной машины для одного входного сигнала. Чтобы адекватно функционировать, она должна показывать удовлетворительную производительность для целого класса входных сигналов, а это будет означать удовлетворительную производительность для класса входных сигналов, получение которого ожидается статистически... Ho6epr Винер (Norbert Wiener), Кибернетика
Во всех компьютерных системах ресурсы ограничены. Ведь любой объем памяти конечен, как и количество устройств ввода-вывода, портов, аппаратных прерываний и процессоров. Если в среде ограниченных аппаратных ресурсов приложение состоит из нескольких процессов и потоков, то эти составляющие должны конкурировать за память, периферийные устройства и процессорное время. Когда и как долго процесс или поток будет использовать системные ресурсы, определяет операционная система. При использовании приоритетного планирования операционная система может прерывать выполняющийся процесс или поток, чтобы удовлетворить все остальные процессы и потоки, соревнующиеся за системные ресурсы. Процессам и потокам приходится также соперничать за программные ресурсы и ресурсы данных. Примерами программных ресурсов служат разделяемые библиотеки (которые предоставляют в общее пользование набор процедур или функций для процессов и потоков), а также приложения, программы и утилиты. При совместном использовании программных ресурсов в памяти содержится только одна копия программного кода. Под ресурсами данных подразумеваются объекты, системные данные (например, переменные среды), файлы, глобально определенные переменные и структуры данных. Что касается ресурсов данных, то процессы и потоки могут иметь собственные копии данных. В других случаях желательно и, возможно, даже необходимо, чтобы данные были разделяемыми. Одни процессы и потоки, работая вместе, используют ограниченные системные ресурсы в определенном порядке, в то время как другие действуют независимо и асинхронно, соревнуясь за использование разделяемых pecypсов. Для управления процессами и потоками, конкурирующими за использование данных, программист может задействовать ряд специальных методов и механизмов.
Синхронизация также необходима для координации порядка выполнения параллельных задач. Примером может служить модель «изготовитель-потребитель», которая рассмотрена в главе 4. «Изготовитель» обязательно начинает выполняться до «потребителя», но не обязательно завершается до него. Подобные задачи нуждаются в синхронизации Синхронизация данных (синхронизация доступа к данным) и задач (синхронизация последовательностей инструкций) — два типа синхронизации, которые необходимо обеспечить при выполнении нескольких параллельных задач.
Координация порядка выполнения потоков
Предположим, у нас есть три параллельно выполняющихся потока — А, В и С. Все они участвуют в обработке списка. Список необходимо отсортировать, выполнить в нем операции поиска и вывода результатов. Каждому потоку назначается отдельная задача. Так, поток А должен отобразить результаты поиска, В — отсортировать список, а С — провести поиск. Сначала список необходимо отсортировать, затем выполнить несколько параллельных операций поиска, а уж потом отобразить результаты. Если задачи, выполняемые потоками, не синхронизировать надлежащим образом, то поток А может попытаться отобразить еще не сгенерированные результаты, что нарушит постусловие, или выхо д ное условие (postcondition), процесса. Предусловием, или вхо д ным условием (precondition), з д есь является необходимость получения
отсортированно г о списка до выполнения в нем поиска. Поиск в неотсортированном списке может дать неверные результаты. Поэтому для этих трех потоков необходимо обеспечить синхронизацию задач, которая приводит в исполнение постусловия и предусловия ло г ических процессов. UML-диаграмма видов деятельности для этого процесса представлена на рис. 5 .1.Сначала поток В должен отсортировать список, затем эстафета управления передается «мно г оканальному» поиску, порождаемому потоком С. И только после завершения поисковых работ по всем направлениям поток А отображает результаты поиска.
Взаимоотношения между синхронизируемыми задачами
Существует четыре основных типа отношений синхронизации между любыми двумя потоками в одном процессе или между любыми двумя процессами в одном приложении: старт-старт (CC), финиш-старт (ФС), старт-финиш (СФ) и финиш-финиш (ФФ). С помощью этих основных типов отношений можно охарактеризовать координацию задач между потоками и процессами. UML-диаграмма видов деятельности для каждого типа отношений синхронизации показана на рис. 5.2.
Рис. 5.1. Диаграмма видов деятельности для задач сортировки списка, поиска и отображения результатов поиска
Отношения типа старт-старт (CC)
В отношениях синхронизации типа старт-старт одна задача не может начаться до тех пор, пока не начнется другая. Одна задача может начаться раньше другой, нo не позже. Предположим, у нас есть программа, которая реализует инкарнацию (воплощение). Инкарнация «материализуется» в виде говорящей головы, созданной, разумеется, компьютерной программой. Инкарнация обеспечивает своего рода «одушевление» программ. Программа, которая реализует «одушевление», имеет несколько потоков. Здесь нас в первую очередь интересует поток А, который «отвечает» за анимацию результата, и поток В, который управляет звуком, или голосом, говорящей головы. Мы хотим создать иллюзию синхронизации звука и движений рта. В идеале они должны происходить абсолютно одновременно. При наличии нескольких процессоров оба потока могут начинаться одновременно. Эти потоки связаны отношением типа старт - старт. В соответствии с условиями временной синхронизации допускается, чтобы поток А начинался немного раньше потока В (именно немного — иначе будет нарушена иллюзия одновременности), но поток В не может начаться раньше потока А. Голос должен ожидать анимацию, а не наоборот. Совершенно нежелательно услышать голос до того, как зашевелятся губы (если это не синхронное озвучивание).
Рис. 5.2. Возможные отношения синхронизации между задачами А и В
Отношения типа финиш-старт (ФС)
В отношениях синхронизации типа финиш-старт задача А не может завершиться до тех пор, пока не начнется задача В. Этот тип отношений типичен для родительско-сыновних процессов. Родительский процесс не может завершить выполнение некоторых операций до тех пор, пока не будет сгенерирован сыновний процесс или пока не будет получена обратная связь от сыновнего процесса, который начал выполнение. Сыновний процесс, «просигналивший» родителю или предоставивший ему необходимую информацию, продолжает выполняться, а родительский процесс после этого может завершиться.
Отношения типа старт-финиш (СФ)
Отношения типа старт-финиш можно считать обратным варианто м отношений типа финиш-старт. В отношениях синхронизации типа старт-финиш одна задача не м ожет начаться до тех пор, пока не завершится другая. Задача А не может начать выпо л нение до тех пор, пока задача В не финиширует или не завершит выполнение определенной операции. Если процесс А считывает данные из канала, связанного с процессом В, то процесс В должен записать данные в канал, прежде чем процесс А начнет считывать из него данные. Процесс В должен завершить по крайней мере одну операцию, записав в канал один элемент, прежде чем начнет действовать процесс А. Потоки, действующие по принципу «производитель-потребитель», — это еще один пример взаимоотношений типа финиш-старт. Потоки, обслуживающие сортировку и поиск элементов в списке (см. рис. 5.1), также демонстрируют этот тип отношений. Прежде чем начнут действовать потоки, реализующие поиск, должен завершить свою работу поток сортировки. Во всех этих случаях один поток или процесс должен завершить свою операцию, прежде чем другой попытается выполнить свою задачу. Если работа потоков не будет скоординирована, глобальная цель потока, процесса или приложения достигнута не будет или же будут получены ошибочные результаты.
Отношения типа старт-финиш обычно предполагают существование информационной зависимости между задачами. При информационной зависимости для корректной работы потоков или процессов необходимо обеспечить межпоточное или межпроцессное взаимодействие. Например, поток поиска данных в списке сгенерирует некорректные результаты, если не будет выполнена сортировка списка. И поток-«потребитель» не получит файлы для обработки, если поток-«производитель» не подготовит их для потребителя.
Отношения типа финиш-финиш (ФФ)