Класс Monitor и блоки синхронизации
Вероятно, самой популярной из гибридных конструкций синхронизации потоков
является класс
Monitor
, обеспечивающий взаимоисключающее блокирование с за-
цикливанием, владением потоком и рекурсией. Данная конструкция используется
чаще других потому, что является одной из самых старых, для ее поддержки в C#
существует встроенное ключевое слово, с ней по умолчанию умеет работать JIT-
компилятор, а CLR пользуется ею от имени приложения. Однако, как вы скоро
убедитесь, работать с ней непросто, а получить некорректный код очень легко.
Сначала мы рассмотрим саму конструкцию, а потом отдельно остановимся на воз-
можных проблемах и способах их обхода.
С каждым объектом в куче может быть связана структура данных, называемая
блоком синхронизации
(sync block). Этот блок содержит поля, похожие на поля
ранее упоминавшегося в этой главе класса
AnotherHybridLock
. Точнее, есть поле
для объекта ядра, идентификатора потока-владельца, счетчика рекурсии и счетчика
ожидающих потоков. Класс
Monitor
является статическим, и его методы принимают
861
Гибридные.конструкции.в.FCL
ссылки на любой объект кучи. Управление полями эти методы осуществляют в бло-
ке синхронизации заданного объекта. Вот как выглядят чаще всего используемые
методы класса
Monitor
:
public static class Monitor {
public static void Enter(Object obj);
public static void Exit(Object obj);
// Можно также указать время блокирования (требуется редко):
public static Boolean TryEnter(Object obj, Int32 millisecondsTimeout);
// Аргумент lockTaken будет рассмотрен позднее
public static void Enter(Object obj, ref Boolean lockTaken);
public static void TryEnter(
Object obj, Int32 millisecondsTimeout, ref Boolean lockTaken);
}
Очевидно, что привязка блока синхронизации к каждому объекту в куче яв-
ляется достаточно расточительной, особенно если учесть тот факт, что большин-
ство объектов никогда не пользуются этим блоком. Чтобы снизить потребление
памяти, разработчики CLR применили более эффективный вариант реализации
описанной функциональности. Во время инициализации CLR выделяется массив
блоков синхронизации. Как уже не раз упоминалось в этой книге, при создании
объекта в куче с ним связываются два дополнительных служебных поля. Первое
поле — указатель на объект-тип — содержит адрес этого объекта в памяти. Вто-
рое поле содержит
индекс блока синхронизации
(sync block index), то есть индекс
в массиве таких блоков.
В момент конструирования объекта этому индексу присваивается значение –1,
что означает отсутствие ссылок на блок синхронизации. Затем при вызове метода
Monitor.Enter
CLR обнаруживает в массиве свободный блок синхронизации и при-
сваивает ссылку на него объекту. То есть привязка объекта к блоку синхронизации
происходит «на лету». Метод
Exit
проверяет наличие потоков, ожидающих блока
синхронизации. Если таких потоков не обнаруживается, метод возвращает индексу
значение –1, означающее, что блоки синхронизации свободны и могут быть связаны
с какими-нибудь другими объектами.
Рисунок 30.1 демонстрирует связь между объектами кучи, их индексами блоков
синхронизации и элементами массива блоков синхронизации в CLR. Указатель на
объект-тип объектов
A
,
B
и
C
ссылается на тип
T
. Это говорит о принадлежности
всех трех объектов к одному и тому же типу. Как обсуждалось в главе 4, объект-
тип также находится в куче и подобно всем остальным объектам обладает двумя
служебными членами: индексом блока синхронизации и указателем на объект-тип.
То есть блок синхронизации можно связать с объектом-типом, а ссылку на этот
объект можно передать методам класса
Monitor
. Кстати, массив блоков синхро-
низации при необходимости может увеличить количество блоков, поэтому не
стоит беспокоиться, что при одновременной синхронизации нескольких объектов
блоков не хватит.
Do'stlaringiz bilan baham: |