Механизмы
синхронизации
Существует
несколько стратегий, которые могут применяться, чтобы разрешать описанные проблемы.
Наиболее распространенным способом является синхронизация потоков. Суть синхронизации
состоит в том, чтобы вынудить один поток ждать, пока другой не закончит какую-то
определенную заранее операцию. Для этой цели существуют специальные синхронизирующие
объекты ядра операционной системы Windows. Они исключают возможность одновременного
доступа к тем данным, которые с ними связаны. Их реализация зависит от конкретной
ситуации и предпочтений программиста, но все они управляют потоками процесса
по принципу: «Не все сразу, по одному, ребята».
MFC предоставляет
несколько классов, реализующих механизмы синхронизации. Прежде всего отметим,
что хорошо спроектированный {thread-safe) класс не должен требовать особых
затрат для синхронизации работы с ним. Все делается внутри класса его методами.
Обычно при создании надежного класса в него изначально внедряют какой-либо синхронизирующий
объект. Например, критическую секцию, событие, семафор, мъютекс или ожидаемый
таймер. Иерархию классов MFC для поддержки синхронизирующих объектов можно
увидеть в MSDN:
Рис. 12.10.
Иерархия классов синхронизации
Все перечисленные
классы, кроме критической секции, принадлежат ядру Windows. Вы знаете, что Windows-приложение
использует множество и других объектов:
- окна, меню, курсоры,
значки, клавиатурные ускорители и т.д. (объекты GUI или Graphics User Intrface);
- перья, кисти, растровые
рисунки, шрифты (объекты GDI Graphics Device Interface).
При работе
с объектами этих подсистем надо соблюдать определенные правила. Но при работе
с объектами ядра правила особые. Вам следует познакомиться с общими положениями
об использовании объектов ядра системы. Они похожи на стандарты СОМ.
- Однажды созданный объект
ядра можно открыть в любом приложении, если оно имеет соответствующие права
доступа к нему.
- Каждый объект ядра имеет
счетчик числа своих пользователей. Как только он станет равным нулю, система
уничтожит объект ядра.
- Обращаться к объекту
ядра надо через описатель (handle), который система дает при создании объекта.
- Каждый объект может
находиться в одном из двух состояний: свободном (signaled) и занятом
(nonsignaled).
Синхронизация
потоков развивается по такому сценарию. При засыпании одного из них операционная
система перестает выделять ему кванты процессорного времени, приостанавливая
его выполнение. Прежде чем заснуть, поток сообщает системе то особое событие,
которое должно разбудить его. Как только указанное событие произойдет, система
возобновит выдачу ему квантов процессорного времени и ноток вновь получит право
на жизнь. Потоки усыпляют себя до освобождения какого-либо синхронизирующего
объекта с помощью двух функций:
DWORD
WaitForSingleObject (HANDLE hObject, DWORD dwTimeOut);
DWORD
WaitForMultipleObjects(DWORD nCount,
CONST
HANDLE* lpHandles, BOOL bWaitAll,
DWORD
dwTimeOut);
Первая функция
приостанавливает поток до тех пор, пока или заданный параметром hObject синхронизирующий
объект не освободится, или пока не истечет интервал времени, задаваемый параметром
dwTimeOut. Если указанный объект в течение заданного интервала не перейдет в
свободное состояние, то система вновь активизирует поток и он продолжит свое
выполнение. В качестве параметра dwTimeOut могут выступать два особых значения:
Таблица
12.3. Значения, выступающие в качестве параметра dwTimeOut
dwTime |
|
0 |
Функция
только проверяет состояние объекта (занят или свободен) и сразу же возвращается
|
INFINITE
|
Время ожидания
бесконечно. Если объект так и не освободится, поток останется в неактивном
состоянии и никогда не получит процессорного времени
|
В соответствии
с причинами, по которым поток продолжает выполнение, функция WaitForSingleObject
может возвращать одно из следующих значений:
Таблица
12.4. Возвращение значений функцией WaitForSingleObject
|
|
WAITJ1MEOUT |
Объект
не перешел в свободное состояние, но интервал времени истек |
WAIT_ABANDONED |
Ожидаемый
объект является мьютексом, который не был освобожден владеющим им потоком
перед окончанием этого потока. Объект мьютекс автоматически переводится
системой в состояние свободен. Такая ситуация называется «отказ от мьютекса»
|
WAIT_OBJECT_0
|
Объект
перешел в свободное состояние |
|
Произошла ошибка,
причину которой можно узнать, вызвав GetLastError
|
Функция WaitForMultipleObjects
задерживает поток и в зависимости от значения флага bWaitAll ждет одного из
следующих событий:
- освобождение хотя бы
одного синхронизирующего объекта из заданного списка;
- освобождение всех указанных
объектов;
- истечение заданного
интервала времени.