-
В тексте этого урока использованы материалы, любезно предоставленные преподавателем СПбГТУ Мариной Полубенцевой, с которой мы совместно ведем курс Visual C++ в Microsoft Certified Educational Center при Санкт-Петербургском государственном техническом университете (www.Avalon.ru).
-
Известно, что в каждый момент времени один процессор может выполнять лишь одну машинную команду. Процессор выполняет команды, последовательно выбирая их одну за другой из памяти в порядке возрастания адресов.
-
Почти все современные операционные системы (Windows 95, Windows NT, Windows 2000, Unix) поддерживают преимущественную многозадачность (preemptive multi-tasking). Этот термин, который часто переводят как вытесняющая многозадачность, означает, что процесс или, точнее, его поток, который в данный момент активен, имеет преимущество перед другими конкурирующими потоками с одинаковым приоритетом.
-
Различают два способа реализации многозадачности: | создать один процесс, имеющий несколько потоков выполнения (threads); | создать несколько процессов, каждый из которых имеет один или несколько потоков выполнения.
-
Часть ОС, называемая системным планировщиком (system scheduler), управляет переключением заданий, определяя, какому из конкурирующих потоков следует выделить следующий квант времени процессора. Решение принимается с учетом приоритетов конкурирующих потоков.
-
Теперь рассмотрим уровни приоритета, которые могут быть присвоены потокам процесса. Внутри каждого процесса, которому присвоен какой-либо класс приоритета, могут существовать потоки, уровень приоритета которых принимает одно из семи возможных значений: | THREAD_PRIORITY_IDLE;
-
Планировщик ОС поддерживает для каждого из базовых уровней приоритета функционирование очереди выполняемых или готовых к выполнению потоков (ready threads queue). Когда процессор становится доступным, то планировщик производит переключение контекстов. Здесь можно выделить такие шаги:
-
Архитектура памяти, используемая операционной системой, – ключ к пониманию того, что в ней происходит. Не имея представления о ней, невозможно ответить на такие вопросы: | Как повысить эффективность приложения? | Как создать данные, разделяемые двумя приложениями?
-
Ниже на рис. 12.8 показано как разбивается память на разделы (partitions) в адресном пространстве процесса под управлением Windows NT. Разделы будем рассматривать, двигаясь сверху вниз, от старших адресов к младшим. Верхнюю половину памяти (от 2 Гбайт до 4 Гбайт) система использует для своих нужд.
-
Операционная система Windows NT представляет собой множество отдельных модулей (подсистем), которые разработаны с учетом двух фундаментальных принципов: | модульность, инкапсуляция, скрытие данных, | некоторые подсистемы функционируют в привилегированном режиме процессора (kernel mode), а остальные в режиме (user mode).
-
В современном операционном окружении программист не может быть уверен и не должен полагаться на то, что коды его программы будут выполняться в тон же последовательности, в какой они написаны. Выполнение одной из функций программы может быть остановлено системой и возобновлено позднее, причем это может произойти даже при выполнении тела какого-либо цикла.
-
Для того чтобы исключить подобный сценарий, автор многопотокового приложения должен решать проблему синхронизации при попытке одновременного доступа к разделяемым ресурсам. Если говорить о файлах с совместным доступом, то сходная ситуация может возникнуть и при столкновении различных процессов, а не только потоков одного процесса.
-
В системе с преимущественной многозадачностью ноток может быть прерван в любой момент. Обычно перед выполнением очередной машинной команды система смотрит, есть ли прерывание. Если есть и приоритет его достаточно высок, то текущая команда не выполняется, а система переходит в режим обработки прерывания.
-
Выше мы рассмотрели, как потоки одного процесса могут вступить в конфликт и испортить работу друг друга. Одной из задач программиста является обеспечить невозможность такого сценария. Другими возможными неприятностями могут быть: рассинхронизация (race conditions) и тупиковая ситуация (deadlock).
-
Существует несколько стратегий, которые могут применяться, чтобы разрешать описанные проблемы. Наиболее распространенным способом является синхронизация потоков. Суть синхронизации состоит в том, чтобы вынудить один поток ждать, пока другой не закончит какую-то определенную заранее операцию.
-
Это самые простые объекты ядра Windows, которые не снижают общей эффективности приложения. Пометив блок кодов в качестве critical section, можно синхронизировать доступ к нему от нескольких потоков. Сначала следует объявить глобальную структуру: | CRITICAL_SECTION cs;
-
Критические секции просты в использовании и обладают высоким быстродействием, но не обладают гибкостью в управлении. Нет, например, возможности установить время блокирования или присвоить имя критической секции для того, чтобы два разных процесса могли иметь с ней дело.
-
В некоторых случаях потоку необходимо ждать, пока другие-потоки не завершат выполнение каких-то операций или не произойдет какое-либо событие (UI-событие User Interface), то есть событие, инициированное пользователем.
-
Семафором называется объект ядра, который позволяет только одному процессу или одному потоку процесса обратиться к критической секции – блоку кодов, осуществляющему доступ к объекту. Серверы баз данных используют их для защиты разделяемых данных.
-
Для того чтобы записи-фантомы не создавались, надо избегать блокирования отдельных записей. Альтернативой является блокировка всей таблицы. Но это решение приводит к снижению эффективности работы СУБД. Другим выходом является предикатная блокировка (predicate locking).
-
Единственным способом выхода из тупиковой ситуации (deadlock) является снятие блокировки одним из потоков. Это означает прерывание или отказ от транзакции. Система может либо предупреждать захваты либо допускать их, но затем соответствующим образом обрабатывать.