что такое критическая секция
СОДЕРЖАНИЕ
Потребность в критических секциях
Тщательно контролируя, какие переменные изменяются внутри и вне критической секции, предотвращается одновременный доступ к общей переменной. Критическая секция обычно используется, когда многопоточная программа должна обновить несколько связанных переменных без того, чтобы отдельный поток вносил конфликтующие изменения в эти данные. В связанной ситуации может использоваться критическая секция, чтобы гарантировать, что общий ресурс, например, принтер, может быть доступен только одному процессу за раз.
Реализация критических секций
Реализация критических секций различается в зависимости от операционной системы.
Критическая секция обычно завершается за конечное время, и поток, задача или процесс должны будут ждать фиксированное время, чтобы войти в нее ( ограниченное ожидание ). Чтобы гарантировать исключительное использование критических секций, требуется некоторый механизм синхронизации при входе и выходе из программы.
Как показано на рис. 2, в случае взаимного исключения (Mutex) один поток блокирует критический раздел, используя методы блокировки, когда ему требуется доступ к общему ресурсу, а другим потокам приходится ждать своей очереди для входа в раздел. Это предотвращает конфликты, когда два или более потока совместно используют одно и то же пространство памяти и хотят получить доступ к общему ресурсу.
Использование критических секций
Критические разделы уровня ядра
Обычно критические секции предотвращают миграцию потоков и процессов между процессорами и вытеснение процессов и потоков прерываниями и другими процессами и потоками.
Критические секции часто допускают вложение. Вложенность позволяет входить и выходить из нескольких критических секций с небольшими затратами.
Если планировщик прерывает текущий процесс или поток в критическом разделе, планировщик либо разрешит текущему выполняющемуся процессу или потоку выполнить работу до завершения критического раздела, либо запланирует процесс или поток для другого полного кванта. Планировщик не будет переносить процесс или поток на другой процессор и не будет планировать запуск другого процесса или потока, пока текущий процесс или поток находится в критической секции.
Поскольку критические секции могут выполняться только на том процессоре, в который они введены, синхронизация требуется только внутри исполняющего процессора. Это позволяет входить и выходить из критических секций практически с нулевыми затратами. Никакой межпроцессорной синхронизации не требуется. Требуется только синхронизация потока инструкций. Большинство процессоров обеспечивают необходимую синхронизацию путем простого прерывания текущего состояния выполнения. Это позволяет критическим секциям в большинстве случаев быть не чем иным, как подсчетом введенных критических секций на процессор.
Улучшения производительности включают выполнение ожидающих прерываний при выходе из всех критических секций и возможность запуска планировщика при выходе из всех критических секций. Кроме того, ожидающие прерывания могут быть переданы для выполнения другим процессорам.
Критические секции не должны использоваться как долговременный блокирующий примитив. Критические разделы должны быть достаточно короткими, чтобы их можно было вводить, выполнять и выходить без каких-либо прерываний со стороны оборудования и планировщика.
Критические разделы в структурах данных
Критические разделы в компьютерных сетях
Класс critical_section
Не допускающий повторные входы мьютекс, который явно учитывает среду выполнения с параллелизмом.
Синтаксис
Члены
Общедоступные определения типов
Открытые классы
Имя | Описание: |
---|---|
Класс critical_section::scoped_lock | Защищенная от исключения оболочка RAII для critical_section объекта. |
Открытые конструкторы
critical_section
Открытые методы
name | Описание: |
---|---|
lock | Получает этот критический раздел. |
native_handle | Возвращает собственный машинный код, зависящий от платформы, если он существует. |
try_lock | Пытается получить блокировку без блокировки. |
try_lock_for | Пытается получить блокировку без блокировки в течение указанного числа миллисекунд. |
блокирован | Разблокирует критическую секцию. |
Remarks
Дополнительные сведения см. в разделе структуры данных синхронизации.
Иерархия наследования
Требования
Заголовок: ConcRT. h
Пространство имен: параллелизм
critical_section
Конструирует новую критическую секцию.
Уничтожает критическую секцию.
Remarks
Ожидается, что блокировка больше не удерживается при выполнении деструктора. Разрешение критической секции, уничтожения с блокировкой, по-прежнему приводит к неопределенному поведению.
скрыть
Получает этот критический раздел.
Remarks
Часто безопаснее использовать конструкцию scoped_lock для получения и освобождения critical_section объекта в безопасном для исключения виде.
native_handle
Возвращает собственный машинный код, зависящий от платформы, если он существует.
Возвращаемое значение
Ссылка на критическую секцию.
Remarks
critical_section объект не связан с машинным кодом, зависящим от платформы, для операционной системы Windows. Метод просто возвращает ссылку на сам объект.
Класс critical_section:: scoped_lock
Защищенная от исключения оболочка RAII для critical_section объекта.
scoped_lock:: scoped_lock
Создает scoped_lock объект и получает critical_section объект, переданный в _Critical_section параметре. Если критическая секция удерживается другим потоком, этот вызов блокируется.
Параметры
_Critical_section
Критическая секция для блокировки.
scoped_lock::
Уничтожает scoped_lock объект и освобождает критическую секцию, заданную в его конструкторе.
try_lock
Пытается получить блокировку без блокировки.
Возвращаемое значение
try_lock_for
Пытается получить блокировку без блокировки в течение указанного числа миллисекунд.
Параметры
_Timeout
Количество миллисекунд перед истечением времени ожидания.
Возвращаемое значение
Критическая секция
Из Википедии — свободной энциклопедии
Критическая секция — участок исполняемого кода программы, в котором производится доступ к общему ресурсу (данным или устройству), который не должен быть одновременно использован более чем одним потоком выполнения. При нахождении в критической секции двух (или более) потоков возникает состояние «гонки» («состязания»). Во избежание данной ситуации необходимо выполнение четырех условий:
Критическая секция (англ. critical section ) — объект синхронизации потоков, позволяющий предотвратить одновременное выполнение некоторого набора операций (обычно связанных с доступом к данным) несколькими потоками. Критическая секция выполняет те же задачи, что и мьютекс.
Между мьютексом и критической секцией есть терминологические различия: так процедура, аналогичная «захвату» мьютекса, называется входом в критическую секцию (англ. enter ), а аналогичная снятию блокировки мьютекса — выходом из критической секции (англ. leave ).
Процедура входа и выхода из критических секций обычно занимает меньшее время, нежели аналогичные операции мьютекса, что связано с отсутствием необходимости обращаться к ядру ОС.
В операционных системах семейства Microsoft Windows разница между мьютексом и критической секцией в том, что мьютекс является объектом ядра и может быть использован несколькими процессами одновременно, критическая секция же принадлежит процессу и служит для синхронизации только его потоков.
Критические секции Windows имеют оптимизацию, заключающуюся в использовании атомарно изменяемой переменной наряду с объектом «событие синхронизации» ядра. Захват критической секции означает атомарное увеличение переменной на 1. Переход к ожиданию на событии ядра осуществляется только в случае, если значение переменной до захвата было уже больше 0, то есть происходит реальное «соревнование» двух или более потоков за ресурс.
Таким образом, при отсутствии соревнования захват/освобождение критической секции обходятся без обращений к ядру.
Кроме того, захват уже занятой критической секции до обращения к ядру какое-то малое время ждёт в цикле (кол-во итераций цикла (англ. spin count ) задаётся функциями InitializeCriticalSectionAndSpinCount() или SetCriticalSectionSpinCount()) опроса переменной числа текущих пользователей, и, если эта переменная становится равной 0, то захват происходит без обращений к ядру.
Сходный объект в ядре Windows называется FAST_MUTEX (ExAcquire/ReleaseFastMutex). Он отличается от критической секции отсутствием поддержки рекурсивного повторного захвата тем же потоком.
Аналогичный объект в Linux называется фьютекс.
Объекты критических секций
Объект критической секции обеспечивает синхронизацию, аналогичную той, которая предоставляется объектом Mutex, за исключением того, что критическая секция может использоваться только потоками одного процесса. Объекты критических секций нельзя совместно использовать в процессах.
Объекты событий, мьютексов и семафоров также можно использовать в однопроцессном приложении, но критически важные объекты раздела обеспечивают несколько более быстрый и эффективный механизм взаимной синхронизации (инструкции по тестированию и определению конкретного процессора). Как и объект Mutex, объект критической секции может принадлежать только одному потоку за раз, что позволяет защитить общий ресурс от одновременного доступа. В отличие от объекта Mutex, невозможно определить, был ли прерван критический раздел.
Начиная с Windows Server 2003 с пакетом обновления 1 (SP1), потоки, ожидающие критической секции, не получают критический раздел на основе первых поступающих. Это изменение значительно повышает производительность для большинства кодов. Однако некоторые приложения зависят от порядка «первым поступил — первым обслужен» (FIFO) и могут работать плохо или не вообще на всех текущих версиях Windows (например, приложения, использующие критические разделы в качестве ограничения). Чтобы обеспечить правильную работу кода, может потребоваться добавить дополнительный уровень синхронизации. Например, предположим, что у вас есть поток-производитель и поток-потребитель, который использует объект критического раздела для синхронизации своей работы. Создайте два объекта событий — по одному для каждого потока, чтобы сообщить о готовности для продолжения работы другого потока. Поток-потребитель ждет, пока производитель сообщит о своем событии перед входом в критическую секцию, и поток-производитель ожидает, пока поток-потребитель не сообщит о своем событии перед входом в критическую секцию. После того как каждый поток покидает критическую секцию, он сигнализирует своему событию освободить другой поток.
Windows Server 2003 и Windows XP: Потоки, ожидающие критического раздела, добавляются в очередь ожидания. они пробуждении и обычно запрашивают критическую секцию в том порядке, в котором они были добавлены в очередь. Однако если потоки добавляются в эту очередь с достаточной скоростью, производительность может быть снижена из-за времени, затрачиваемого на пробуждение каждого ожидающего потока.
Поток использует функцию EnterCriticalSection или трентеркритикалсектион для запроса владения критическим разделом. Она использует функцию LeaveCriticalSection для освобождения владения критическим разделом. Если объект критического раздела в настоящее время принадлежит другому потоку, EnterCriticalSection ожидает неопределенного времени владения. В отличие от этого, если объект Mutex используется для взаимного исключения, функции Wait принимают заданный интервал времени ожидания. Функция трентеркритикалсектион пытается войти в критическую секцию, не блокируя вызывающий поток.
Поток использует функцию инитиализекритикалсектионандспинкаунт или сеткритикалсектионспинкаунт для задания счетчика прокрутки для объекта критической секции. Цикличность означает, что когда поток пытается получить критический раздел, который заблокирован, поток входит в цикл, проверяет, снята ли блокировка, и если блокировка не была снята, поток переходит в спящий режим. В однопроцессорных системах счетчик пропусков игнорируется, а для счетчика критического раздела — значение 0 (ноль). В многопроцессорных системах, если критический раздел недоступен, вызывающий поток вращает двспинкаунт раз перед выполнением операции ожидания семафора, связанного с критическим разделом. Если критическая секция будет свободна во время операции Spin, вызывающий поток не будет выполнять операцию ожидания.
Любой поток процесса может использовать функцию делетекритикалсектион для освобождения системных ресурсов, выделенных при инициализации объекта критической секции. После вызова этой функции объект критической секции не может быть использован для синхронизации.
При владении объектом критической секции единственными затронутыми потоками являются потоки, ожидающие владение вызовом EnterCriticalSection. Потоки, которые не ожидают ожидания, могут продолжать выполняться.
Критическая секция
Понятие критической секции подразумевает, что единовременно только один поток получал доступ к определенному ресурсу (участку кода, манипулирующему с данным ресурсом). Система может в любой момент вытеснить поток, находящийся в критической, но ни один из потоков, которым нужен занятый ресурс, не получит процессорное время до тех пор, пока владеющим им поток не выйдет за границы критической секции.
Название критическая секция используется и для объекта синхронизации – структуры (типа данных) Windows, используемой для организации критической секции в изложенном выше смысле.
Для реализации взаимного исключения с помощью критической секции необходимо глобально (обеспечить доступность из всех потоков процесса, конкурирующих за ресурс) объявить переменную типа CRITICAL_SECTION. Структура CRITICAL_SECTION представляет для программиста «черный ящик», то есть не происходит прямого обращения к полям данной структуры. Работа с CRITICAL_SECTION осуществляется исключительно через специальные функции, которым передается адрес соответствующего экземпляра данной структуры.
Перед началом использования (до обращения какого-либо потока к защищенному ресурсу) элементы структуры необходимо инициализировать с помощью вызова:
void InitializeCriticalSection( LPCRITICAL_SECTION lpCriticalSection);
Захват критической секции производится с помощью функций:
void EnterCriticalSection( LPCRITICAL_SECTION lpCriticalSection );
BOOL TryEnterCriticalSection( LPCRITICAL_SECTION lpCriticalSection );
При вызове EnterCriticalSection происходит анализ элементов структуры CRITICAL_SECTION. Если ресурс занят, в них содержатся сведения о том, какой поток пользуется ресурсом. EnterCriticalSection выполняет следующие действия.
Если ресурс свободен, EnterCriticalSection модифицирует элементы структуры, указывая, что вызывающий поток занимает ресурс, после чего немедленно возвращает управление, и поток продолжает свою работу (получив доступ к ресурсу).
Если значения элементов структуры свидетельствуют, что ресурс уже захвачен вызывающим потоком, EnterCriticalSection обновляет их, отмечая тем самым, сколько раз подряд этот поток захватил ресурс, и возвращает управление потоку. Таким образом, поток, владеющий критической секцией, может повторно войти в критическую секцию, что дает возможность организации рекурсивных функций.
Использование функции TryEnterCriticalSection позволяет опросить критическую секцию, для проверки занята она другим потоком или нет. Возвращение данной функцией значения True свидетельствует о том, что вызывающий поток приобрел права владения критическим участком кода, тогда как значение False говорит о том, что данный критический участок кода уже принадлежит другому потоку. Функция TryEnterCriticalSection никогда не приостанавливает выполнение вызывающего потока, позволяя проверить доступность ресурса и в случае его занятости обработать такую ситуацию соответствующим образом.
В конце участка кода, использующего разделяемый ресурс, должен присутствовать
void LeaveCriticalSection( LPCRITICAL_SECTION lpCriticalSection );
Эта функция просматривает элементы структуры CRITICAL_SECTION и уменьшает счетчик числа захватов ресурса вызывающим потоком на 1. Если его значение больше 0, LeaveCriticalSection ничего не делает и просто возвращает управление. Если значение счетчика достигло 0, LeaveCriticalSection сначала выясняет, есть ли в системе другие потоки, ждущие данный ресурс в вызове EnterCriticalSection. Если есть хотя бы один такой поток, функция настраивает значения элементов структуры, чтобы они сигнализировали о занятости ресурса, и отдает его одному из ожидающих потоков при присутствии таковых. LeaveCriticalSection никогда не приостанавливает поток, а управление возвращает немедленно.
При завершении работы с критической секцией для нее необходимо вызвать функцию
void DeleteCriticalSection( LPCRITICAL_SECTION lpCriticalSection );
Данная функция сбрасывает все переменные-члены внутри структуры CRITICAL_SECTION. Естественно, нельзя удалять критическую секцию в тот момент, когда ею все еще пользуется какой-либо поток.
В силу своей простоты и преимуществ в отношении производительности критические секции являются предпочтительным механизмом синхронизации, если из возможностей достаточно для того, чтобы удовлетворить требования, предъявляемые решаемой задачей.
Однако критическая секция имеет один существенный недостаток: она не может быть использована для синхронизации потоков принадлежащих различным процессам.