- •26. Синхронизация
- •27. Оповещения и асинхронные вызовы процедур
- •28. Структура процессов
- •Требования подсистем среды
- •29. Базовая структура процессов
- •Управление клиентскими процессами
- •Как предотвратить неправильное использование
- •52. База данных страничных фреймов
- •53. Дескрипторы виртуальных адресов
- •Соображения мультипроцессорной обработки
- •Соображения переносимости
- •Общие сведения
- •Планирование потоков
- •55. Объекты процесс ядра и поток ядра
- •56. Приоритеты планирования
- •Переключение контекста
- •57. Обработка прерываний и исключений
- •Обработчик ловушки
- •Распределение прерываний
- •58. Таблица распределения прерываний. Программные прерывания. Прерывания асинхронного вызова процедур (apc).
- •Прерывания асинхронного вызова процедуры (арс)
- •59. Распределение исключений
- •60. Многопроцессорная синхронизация
- •Синхронизация на уровне ядра
- •61. Синхронизация на уровне исполнительной системы
- •Восстановление после сбоя питания
26. Синхронизация
При работе параллельного приложения его потокам часто требуется способ связи друг с другом для координации своих действий. Пример такой связи — передача данных через каналы. Однако простейшей формой связи является синхронизация (synchronization). Синхронизация означает способность потока добровольно приостанавливать свое исполнение и ожидать, пока не завершится выполнение некоторой операции другим потоком.
В приведенном выше примере с компилятором препроцессор считывает исходный код на С и помещает результаты его обработки в буфер памяти, который он использует совместно с компилятором. Последний принимает результаты препроцессора в качестве исходных данных, выполняет компиляцию и генерирует объектный код. После запуска программы поток компилятора должен ждать, пока поток препроцессора поместит что-либо в буфер, и лишь затем читать данные. Аналогично, когда весь буфер заполнен, препроцессор должен ждать, пока компилятор не выберет данные из буфера, прежде чем записывать туда новую информацию.
Все ОС, поддерживающие многозадачность или мультипроцессорную обработку, должны предоставлять потокам способ ожидания того, что другой поток что-либо сделает: например, освободит накопитель на магнитной ленте или закончит запись в совместно используемый буфер памяти. ОС должна также дать потоку возможность сообщить другим потокам об окончании выполнения операции. Получив такое уведомление, ожидающий поток может продолжить выполнение.
Средства ожидания и сообщения реализованы в исполнительной системе NT как часть объектной архитектуры. Синхронизационные объекты (synchronization objects) — это объекты исполнительной системы, при помощи которых поток синхронизирует свое выполнение. К их числу относятся следующие объекты
• Процесс
• Поток
• Файл
• Событие
• Пара событий
• Семафор
• Таймер
• Мутант
Первые три из перечисленных выше объектов выполняют и другие функции, в то время как последние пять существуют исключительно для поддержки синхронизации. При помощи этих объектов исполнительной системы потоки могут координировать свое выполнение с разнообразными происшествиями в системе, используя различные правила для различных ситуаций.
В любой момент времени синхронизационный объект находится в одном из двух состояний: свободен (signaled state) либо занят (nonsignaled state). Состояние "свободен" определено по-разному для разных объектов. Объект-поток находится в состоянии "занят" все время существования, но устанавливается системой в состояние "свободен", когда его выполнение завершается. Аналогично, ядро устанавливает процесс в состояние "свободен", когда завершается его последний поток. В противоположность этому, объект-таймер "срабатывает" через заданное время (по истечении этого времени ядро устанавливает объект-таймер в состояние "свободен").
Для синхронизации с объектом поток вызывает один из системных сервисов ожидания, предоставляемых диспетчером объектов, и передает описатель данного объекта. Поток может ждать один или несколько объектов, а также задать отмену ожидания, если оно не закончилось за некоторый промежуток времени. Всякий раз, когда ядро устанавливает объект в состояние "свободен", оно проверяет, есть ли потоки, ожидающие этот объект. Если такие потоки есть, то ядро выводит один или несколько из них из состояния ожидания, и они могут продолжить выполнение.
При выборе механизма синхронизации следует учитывать правила, управляющие поведением различных синхронизационных объектов. Закончится ли ожидание потока, когда объект, у которого он ждет, будет переведен в состояние "свободен", зависит от типа объекта, как показано в табл. 4-3.
Таблица 4-3. Определения состояния "свободен"
Тип объекта |
Устанавливается в состояние "свободен", когда: |
Как влияет на ожидающие потоки: |
Процесс |
завершается последний поток |
все освобождаются |
Поток |
завершается исполнение потока |
то же |
Файл |
завершается операция ввода-вывода |
— " — |
Событие |
поток устанавливает событие |
— " — |
Пара событий |
выделенный поток клиента или сервера устанавливает событие |
освобождается другой выделенный поток |
Семафор |
счетчик семафора доходит до нуля |
все освобождаются |
Таймер |
наступает заданное время или |
то же |
Мутант |
истекает временной интервал поток освобождает мутант |
освобождается один поток |
В большинстве случаев, когда объект устанавливается в состояние "свободен", все ожидающие потоки немедленно освобождаются. Например, объект-событие используется для оповещения о том, что произошло некоторое событие. Когда объект-событие устанавливается в состояние "свободен", все ожидающие его потоки освобождаются. Исключением является поток, ожидающий одновременно более одного объекта; такому потоку, возможно, потребуется продолжать ожидание до тех пор, пока и остальные объекты не станут свободными.
В отличие от объекта-события, для объекта-мутанта (для программистов в Win32 он видим как объект-мьютекс) определено понятие владения им. Объект-мутант применяется, чтобы установить взаимоисключающий доступ к ресурсу, и в данный момент времени владеть им может не более одного потока. Когда мутант освобождается, ядро устанавливает его в состояние "свободен", после чего выбирает для исполнения один из потоков, ожидающих его. Поток, выбранный ядром, завладевает мутантом, а все остальные потоки продолжают ждать
Семантика синхронизации исполнительной системы NT видима программистам Win32 посредством функций API WaitForSingleObject() и WaitFor-MultipleObjects(), которые реализованы при помощи вызовов аналогичных системных сервисов диспетчера объектов NT. Поток в приложении Win32 может синхронизироваться с такими объектами Win32, как процесс, поток, событие, семафор, мьютекс или файл. В качестве примера рассмотрим синхронизацию потока в программе электронной таблицы с другим потоком этой программы. Предположим, что у приложения есть основной поток, выполняющий обычные функции работы с электронной таблицей, и вспомогательный поток, выводящий файлы электронной таблицы на принтер. Теперь допустим, что пользователь отправляет электронную таблицу на печать и, прежде чем печать завершится, вводит команду выхода из программы. Основной поток, который получает запрос на выход из программы, не завершает процесс немедленно (хотя он может убрать окно программы с экрана). Вместо этого он вызывает функцию WaitForSingleObject(), чтобы подождать, пока поток спулера не закончит печать и не завершится. После завершения потока спулера основной поток освобождается и завершается, что приводит к выходу из программы электронной таблицы и завершению соответствующего процесса.
