Чтение онлайн

на главную - закладки

Жанры

Программирование на Visual C++. Архив рассылки

Jenter Алекс

Шрифт:

Именно тут приходят на помощь критические секции. Перепишем наш пример.

// Нить #1

void Proc1 {

 ::EnterCriticalSection(&m_lockObject);

 if (m_pObject) m_pObject->SomeMethod;

 ::LeaveCriticalSection(&m_lockObject);

}

// Нить #2

void Proc2(IObject *pNewObject) {

 ::EnterCriticalSection(&m_lockObject);

 if (m_pObject) delete m_pObject;

 m_pObject = pNewobject;

 ::LeaveCriticalSection(&m_lockObject);

}

Код,

помещенный между ::EnterCriticalSection и ::LeaveCriticalSection с одной и той же критической секцией в качестве параметра, никогда не будет выполняться параллельно. Это означает, что если нить #1 успела "захватить" критическую секцию m_lockObject, то при попытке нити #2 заполучить эту же критическую секцию в свое единоличное пользование, ее выполнение будет приостановлено до тех пор, пока нить #1 не "отпустит" m_lockObject при помощи вызова ::LeaveCriticalSection. И наоборот, если нить #2 успела раньше нити #1, то та "подождет", прежде чем начнет работу с m_pObject.

Работа с критическими секциями

Что же происходит внутри критических секций и как они устроены? Прежде всего, следует отметить, что критические секции это не объекты ядра операционной системы. Практически вся работа с критическими секциями происходит в создавшем их процессе. Их этого следует, что критические секции могут быть использованы только для синхронизации в пределах одного процесса. Теперь рассмотрим критические секции поближе.

Структура RTL_CRITICAL_SECTION

typedef struct _RTL_CRITICAL_SECTION {

 PRTL_CRITICAL_SECTION_DEBUG DebugInfo; // Используется операционной системой

 LONG LockCount; // Счетчик использования этой критической секции

 LONG RecursionCount; // Счетцик повторного захвата из нити-владельца

 HANDLE OwningThread; // Уникальный ID нити-владельца

 HANDLE LockSemaphore; // Объект ядра используемый для ожидания

 ULONG_PTR SpinCount; // Количество холостых циклов перед вызовом ядра

} RTL_CRITICAL_SECTION, *PRTL_CRITICAL_SECTION;

Поле LockCount увеличивается на единицу при каждом вызове ::EnterCriticalSection и уменьшается при каждом вызове ::LeaveCriticalSection. Это первая (а часто и единственная проверка) на пути к "захвату" критической секции. Если после увеличения в этом поле находится ноль, это означает, что до этого момента непарных вызовов ::EnterCriticalSection из других ниток не было. В этом случае можно забрать данные, охраняемые этой критической секцией в монопольное пользование. Таким образом, если критическая секция интенсивно используется не более чем одной нитью, ::EnterCriticalSection практически вырождается в ++LockCount, а ::LeaveCriticalSection в – -LockCount. Это очень важно. Это означает, что использование многих тысяч критических секций в одном процессе не повлечет значительного расхода ни системных ресурсов, ни процессорного времени.

СОВЕТ

Не стоит экономить на критических секциях. Много наэкономить все равно не получится.

В поле RecursionCount хранится количество повторных вызовов ::EnterCriticalSection из одной и той же нити. Действительно,

если вызвать ::EnterCriticalSection из одной и той же нити несколько раз, все вызовы будут успешны. Т.е. вот такой код не останосится навечно во втором вызове ::EnterCriticalSection, а отработает до конца.

// Нить #1

void Proc1 {

 ::EnterCriticalSection(&m_lock);

 // ...

 Proc2

 // ...

 ::LeaveCriticalSection(&m_lock);

}

// Все еще нить #1

void Proc2 {

 ::EnterCriticalSection(&m_lock);

 // ...

 ::LeaveCriticalSection(&m_lock);

}

Действительно, критические секции предназначены для защиты данных от доступа из нескольких ниток. Многократное использование одной и той же критической секции из одной нити не приведет к ошибке. Это вполне нормальное явление. Следите, чтобы количество вызовов ::EnterCriticalSection и ::LeaveCriticalSection совпадало, и все будет хорошо.

Поле OwningThread содержит 0 для никем не занятых критических секций или уникальный идентификатор нити-владельца. Это поле проверяется, если при вызове ::EnterCriticalSection поле LockCount, после увеличения на единицу, оказалось больше нуля. Если OwningThread совпадает с уникальным идентификатором текущей нити, то RecursionCount просто увеличивается на единицу и ::EnterCriticalSection возвращается немедленно. Иначе ::EnterCriticalSection будет дожидаться, пока нить, владеющая критической секцией, не вызовет ::LeaveCriticalSection необходимое количество раз.

Поле LockSemaphore используется, если нужно подождать, пока критическая секция освободится. Если LockCount больше нуля и OwningThread не совпадает с уникальным идентификатором текущей нити, то ждущая нить создает объект ядра (событие) и вызывает ::WaitForSingleObject(LockSemaphore). Нить-владелец, после уменьшения RecursionCount, проверяет его, и если значение этого поля равно нулю, а LockCount больше нуля, то это значит, что есть как минимум одна нить, ожидающая, пока LockSemaphore не окажется в состоянии "случилось!". Для этого нить-владелец вызывает ::SetEvent и какая-то одна (только одна) из ожидающих ниток пробуждается и получает доступ к критическим данным.

WindowsNT/2k генерирует исключение, если попытка создать событие не увенчалась успехом. Это верно как для функций ::Enter/LeaveCriticalSection так и для ::InitializeCriticalSectionAndSpinCount с установленным старшим битом параметра SpinCount. Но только не WindowsXP. Разработчики ядра этой операционной системы поступили по-другому. Вместо генерации исключения, функции ::Enter/LeaveCriticalSection, если не могут создать собственное событие, начинают использовать заранее созданный глобальный объект. Один на всех. Таким образом, в случае катастрофической нехватки системных ресурсов, программа под управлением WindowsXP ковыляет какое-то время дальше. Действительно, писать программы, способные продолжать работать после того, как ::EnterCriticalSection сгенерировала исключение, черезвычайно сложно. Как правило, если программистом и предусмотрен такой поворот событий, то дальше вывода сообщения об ошибке и аварийного завершеня программы дело не идет. Как следствие, WindowsXP игнорирует старший бит поля LockCount.

Поделиться:
Популярные книги

Газлайтер. Том 10

Володин Григорий
10. История Телепата
Фантастика:
боевая фантастика
5.00
рейтинг книги
Газлайтер. Том 10

На границе империй. Том 7. Часть 2

INDIGO
8. Фортуна дама переменчивая
Фантастика:
космическая фантастика
попаданцы
6.13
рейтинг книги
На границе империй. Том 7. Часть 2

Звездная Кровь. Изгой

Елисеев Алексей Станиславович
1. Звездная Кровь. Изгой
Фантастика:
боевая фантастика
попаданцы
рпг
5.00
рейтинг книги
Звездная Кровь. Изгой

Хозяин Теней 4

Петров Максим Николаевич
4. Безбожник
Фантастика:
попаданцы
аниме
фэнтези
5.00
рейтинг книги
Хозяин Теней 4

Картофельное счастье попаданки

Иконникова Ольга
Фантастика:
фэнтези
5.00
рейтинг книги
Картофельное счастье попаданки

Экзорцист: Проклятый металл. Жнец. Мор. Осквернитель

Корнев Павел Николаевич
Фантастика:
фэнтези
героическая фантастика
5.50
рейтинг книги
Экзорцист: Проклятый металл. Жнец. Мор. Осквернитель

Доктора вызывали? или Трудовые будни попаданки

Марей Соня
Фантастика:
юмористическая фантастика
попаданцы
5.00
рейтинг книги
Доктора вызывали? или Трудовые будни попаданки

Метатель

Тарасов Ник
1. Метатель
Фантастика:
боевая фантастика
попаданцы
рпг
фэнтези
фантастика: прочее
постапокалипсис
5.00
рейтинг книги
Метатель

Моя на одну ночь

Тоцка Тала
Любовные романы:
современные любовные романы
короткие любовные романы
5.50
рейтинг книги
Моя на одну ночь

Чехов. Книга 2

Гоблин (MeXXanik)
2. Адвокат Чехов
Фантастика:
фэнтези
альтернативная история
аниме
5.00
рейтинг книги
Чехов. Книга 2

Хозяин Теней 2

Петров Максим Николаевич
2. Безбожник
Фантастика:
попаданцы
аниме
фэнтези
5.00
рейтинг книги
Хозяин Теней 2

Сумеречный стрелок 7

Карелин Сергей Витальевич
7. Сумеречный стрелок
Фантастика:
городское фэнтези
попаданцы
аниме
5.00
рейтинг книги
Сумеречный стрелок 7

Жизнь под чужим солнцем

Михалкова Елена Ивановна
Детективы:
прочие детективы
9.10
рейтинг книги
Жизнь под чужим солнцем

Красноармеец

Поселягин Владимир Геннадьевич
1. Красноармеец
Фантастика:
боевая фантастика
попаданцы
4.60
рейтинг книги
Красноармеец