Реализация критической секции: объект-оптекс
Критические секции всегда интересовали меня. В конце концов, ссли это всего лишь объекты пользовательского режима, то почему бы мне не реализовать их самому? Разве нельзя заставить их работать бсз поддержки операционной системы? Кроме того, написав собственную критическую секцию, я мог бы расширить ее функциональность и в чем-то даже усовершенствовать. По крайней мере я сделал бы так, чтобы она отслеживала, какой поток захватывает защищаемый ею ресурс. Такая реализация критической секции помогла бы мне устранять проблемы с взаимной блокировкой потоков с помощью отладчика я узнавал бы, какой из них не освободил тот или иной ресурс.
Так что давайте без лишних разговоров перейдем к тому, как реализуются критические секции. Я все время утверждаю, что они являются объектами пользовательского режима. На самом дслс это не совсем так. Любой поток, который пытается войти в критическую секцию, уже захваченную другим потоком, переводится в состояние ожидания. А для этого он должен перейти из пользовательского режима в режим ядра. Поток пользовательского режима может остановиться, просто войдя в цикл ожидания, но это вряд ли можно назвать эффективной реализацией ждущего режима, и поэтому Вы должны всячески избегать ее.
Значит, в критических секциях есть какой-то объект ядра, умеющий переводить поток в эффективный ждущий режим. Критическая секция обладает высоким быстродействием, потому что этот объект ядра используется только при конкуренции потоков за вход в критическую секцию. И он не задействован, пока потоку удастся немедленно захватывать защищаемый ресурс, работать с ним и освобождать его без конкуренции со стороны других потоков, так как выходить из пользовательского режима потоку в этом случае не требуется. В большинстве приложений конкуренция двух (или более) потоков за одновременный вход в критическую секцию наблюдается нечасто.
Мой вариант критической секции содержится в файлах Optex.h и Optex.cpp (см, листинг на рис. 10-1). Я назвал её оптимизированным мьютексом — оптексом и реализовал в виде С++-класса.
Просмотрев файл Optex.h, Вы увидите, что в основном они являются элементами структуры SHAREDINFO, а остальные — членами самого класса. Назначение каждой переменной описывается в следующей таблице.
Переменная | Описание |
m_lLockCount |
Сообщает, сколько раз потоки пытались занять оптекс Ее значение равно 0, если оптекс не занят ни одним потоком. |
т dwThreadId | Сообщает уникальный идентификатор потока — владельца оптекса Ее значение равно 0, если оптекс не занят ни одним потоком |
m_lRecurseCount | Указывает, сколько раз отеке был занят потоком- владельцем. Ее зна чение равно 0, если оптекс не занят ни одним потоком. |
m_hevt | Содержит описатель объекта ядра "событие", используемого, только если поток пытается войти в оптекс в то время, как им владеет другой поток. Описатели объектов ядра специфичны для конкретных процес сов, и имении поэтому данная переменная не включена в структуру SHAREDINFO. |
m_dwSpinCount | Определяет, сколько попыток входа в оптекс должен предпринять по ток до перехода в состояние ожидания на объекте ядра "событие". На однопроцессорной машине значение этой переменной всегда равно 0. |
m_hfm | Содержит описатель объекта ядра "проекция файла", используемого при разделении оптекса несколькими процессами Описатели объек тов ядра специфичны для конкретных процессов, и именно поэтому данная переменная не включена в структуру SHAREDINFO. В однопро цессном оптексе значение этой переменной всегда равно NULL |
m_psi | Содержит указатель на элементы данных оптекса, которые могут ис пользоваться несколькими процессами. Адреса памяти специфичны для конкретных процессов, и именно поэтому данная переменная не включена в структуру SHAREDINFO. В однопроцессном оптексе эта пе ременная указывает ни блок памяти, выделенный из кучи, а в межпро цессном — на файл, спроецированный в память. |