Помощничек
Главная | Обратная связь


Археология
Архитектура
Астрономия
Аудит
Биология
Ботаника
Бухгалтерский учёт
Войное дело
Генетика
География
Геология
Дизайн
Искусство
История
Кино
Кулинария
Культура
Литература
Математика
Медицина
Металлургия
Мифология
Музыка
Психология
Религия
Спорт
Строительство
Техника
Транспорт
Туризм
Усадьба
Физика
Фотография
Химия
Экология
Электричество
Электроника
Энергетика

Структура 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(), и какой-то один (только один) из ожидающих потоков пробуждается и получает доступ к критическим данным.

И, наконец, поле SpinCount. Это поле используется только многопроцессорными системами. В однопроцессорных системах, если критическая секция занята другим потоком, можно только переключить управление на нее и подождать наступления события. В многопроцессорных системах есть альтернатива: прогнать некоторое количество раз холостой цикл, проверяя каждый раз, не освободилась ли критическая секция. Если за SpinCount раз это не получилось, переходим к ожиданию. Это гораздо эффективнее, чем переключение на планировщик ядра и обратно.[2]

Вывод

Использование механизма критических секций часто бывает необходимо при работе с несколькими потоками. Критические интервалы позволяют избегать ситуации, когда два потока одновременно используют критические ресурсы, а значит гарантирует корректность результатов работы потоков. Важно помнить, что критические секции не являются объектами ядра операционной системы, а являются локальными для каждого из процессов, и что для одного и того же потока вход в критическую секцию возможен неоднократно. Рекомендуется не пытаться экономить ресурсы системы на критических секциях. Много сэкономить все равно не получится, а корректная работа потоков при этом гарантироваться не будет.


 




Поиск по сайту:

©2015-2020 studopedya.ru Все права принадлежат авторам размещенных материалов.