Реферат: Критические секции
{
::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.
И, наконец, поле SpinCount. Это поле используется только многопроцессорными системами. В однопроцессорных системах, если критическая секция занята другой нитью, можно только переключить управление на нее и подождать наступления события. В многопроцессорных системах есть альтернатива: прогнать некоторое количество раз холостой цикл, проверяя каждый раз, не освободилась ли наша критическая секция. Если за SpinCount раз это не получилось, переходим к ожиданию. Это гораздо эффективнее, чем переключение на планировщик ядра и обратно. Кроме того, в WindowsNT/2k старший бит этого поля служит для индикации того, что объект ядра, хендл которого находится в поле LockSemaphore, должен быть создан заранее. Если системных ресурсов для этого недостаточно, система сгенерирует исключение, и программа может "урезать" свою функциональность. Или совсем завершить работу.
ПРИМЕЧАНИЕ Все это верно для Windows NT/2k/XP. В Windows 9x/Me используется только поле LockCount. Там находится указатель на объект ядра, возможно, просто взаимоисключение (mutex). Все остальные поля равны нулю. |
API для работы с критическими секциями
BOOL InitializeCriticalSection(LPCRITICAL_SECTION lpCriticalSection);
BOOL InitializeCriticalSectionAndSpinCount(LPCRITICAL_SECTION lpCriticalSection, DWORD dwSpinCount);
Заполняют поля структуры, адресуемой lpCriticalSection. После вызова любой из этих функций критическая секция готова к работе.
Листинг 1. Псевдокод RtlInitializeCriticalSection из ntdll.dll
VOID RtlInitializeCriticalSection(LPRTL_CRITICAL_SECTION pcs) { RtlInitializeCriticalSectionAndSpinCount(pcs, 0) } VOID RtlInitializeCriticalSectionAndSpinCount( LPRTL_CRITICAL_SECTION pcs, DWORD dwSpinCount) { pcs->DebugInfo = NULL; pcs->LockCount = -1; pcs->RecursionCount = 0; pcs->OwningThread = 0; pcs->LockSemaphore = NULL; К-во Просмотров: 763
Бесплатно скачать Реферат: Критические секции
|