Важным понятием при изучении способов синхронизации процессов является понятие критической секции ( critical section ) программы. Критическая секция – это часть программы, исполнение которой может привести к возникновению race condition для определенного набора программ. Чтобы исключить эффект гонок по отношению к некоторому ресурсу, необходимо организовать работу так, чтобы в каждый момент времени только один процесс мог находиться в своей критической секции , связанной с этим ресурсом. Иными словами, необходимо обеспечить реализацию взаимоисключения для критических секций программ. Реализация взаимоисключения для критических секций программ с практической точки зрения означает, что по отношению к другим процессам, участвующим во взаимодействии, критическая секция начинает выполняться как атомарная операция . Давайте рассмотрим следующий пример, в котором псевдопараллельные взаимодействующие процессы представлены действиями различных студентов ( таблица 5.1):
Здесь критический участок для каждого процесса – от операции «Обнаруживает, что хлеба нет» до операции «Возвращается в комнату» включительно. В результате отсутствия взаимоисключения мы из ситуации «Нет хлеба» попадаем в ситуацию «Слишком много хлеба». Если бы этот критический участок выполнялся как атомарная операция – «Достает два батона хлеба», то проблема образования излишков была бы снята.
53 Что такое критическая секция в C#
17-05 | Приходит в комнату |
17-07 | Обнаруживает,что хлеба нет |
17-09 | Уходит в магазин |
17-11 | Приходит в комнату |
17-13 | Обнаруживает, что хлеба нет |
17-15 | Уходит в магазин |
17-17 | Приходит в комнату |
17-19 | Обнаруживает,что хлеба нет |
17-21 | Уходит в магазин |
17-23 | Приходит в магазин |
17-25 | Покупает 2 батона на всех |
17-27 | Уходит из магазина |
17-29 | Приходит в магазин |
17-31 | Покупает 2 батона на всех |
17-33 | Уходит из магазина |
17-35 | Приходит в магазин |
17-37 | Покупает 2 батона на всех |
17-39 | Уходит из магазина |
17-41 | Возвращается в комнату |
17-43 | |
17-45 | |
17-47 | Возвращается в комнату |
17-49 | |
17-51 | |
17-53 | Возвращается в комнату |
Сделать процесс добывания хлеба атомарной операцией можно было бы следующим образом: перед началом этого процесса закрыть дверь изнутри на засов и уходить добывать хлеб через окно, а по окончании процесса вернуться в комнату через окно и отодвинуть засов. Тогда пока один студент добывает хлеб, все остальные находятся в состоянии ожидания под дверью ( таблица 5.2).
Урок 36. Критическая секция и синхронизация
17-05 | Приходит в комнату |
17-07 | Достает два батона хлеба |
17-43 | Приходит в комнату |
17-47 | Приходит в комнату |
Итак, для решения задачи необходимо, чтобы в том случае, когда процесс находится в своем критическом участке , другие процессы не могли войти в свои критические участки . Мы видим, что критический участок должен сопровождаться прологом ( entry section ) – «закрыть дверь изнутри на засов» – и эпилогом ( exit section ) – «отодвинуть засов», которые не имеют отношения к активности одиночного процесса. Во время выполнения пролога процесс должен, в частности, получить разрешение на вход в критический участок , а во время выполнения эпилога – сообщить другим процессам, что он покинул критическую секцию .
В общем случае структура процесса, участвующего во взаимодействии, может быть представлена следующим образом:
while (some condition)
Здесь под remainder section понимаются все атомарные операции , не входящие в критическую секцию .
Оставшаяся часть этой лекции посвящена различным способам программной организации пролога и эпилога критического участка в случае, когда очередность доступа к критическому участку не имеет значения.
Источник: intuit.ru
Критическая секция
Критическая секция (critical section) — это участок кода, требующий монопольного доступа к каким-то общим данным (ресурсам); параллельное выполнение этого участка кода несколькими потоками может привести к непредсказуемым или неверным результатам.
Понятие критической секции подразумевает, что единовременно только один поток получал доступ к определенному ресурсу (участку кода, манипулирующему с данным ресурсом). Система может в любой момент вытеснить поток, находящийся в критической, но ни один из потоков, которым нужен занятый ресурс, не получит процессорное время до тех пор, пока владеющим им поток не выйдет за границы критической секции.
Название критическая секция используется и для объекта синхронизации – структуры (типа данных) 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 обновляет их, отмечая тем самым, сколько раз подряд этот поток захватил ресурс, и возвращает управление потоку. Таким образом, поток, владеющий критической секцией, может повторно войти в критическую секцию, что дает возможность организации рекурсивных функций.
Если значения элементов структуры указывают на то, что ресурс занят другим потоком, EnterCriticalSection переводит вызывающий поток в режим ожидания. Система запоминает, что данный поток хочет получить доступ к ресурсу, и — как только поток, занимавший критическую секцию, освобождает ее, ожидающий поток переходит в активное состояние и занимает критическую секцию.
Интересным фактом является то, что потоки, ожидающие освобождения критической секции, на самом деле не блокируются «навечно». Данная функция реализована таким образом, что по истечении определенного времени, генерирует исключение (ожидание прекращается ошибкой). Длительность времени ожидания функцией EnterCriticalSection определяется значением системного реестра Windows. Длительность времени ожидания измеряется в секундах и по умолчанию равна 2 592 000 секунд (что составляет ровно 30 суток).
Использование функции 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. Естественно, нельзя удалять критическую секцию в тот момент, когда ею все еще пользуется какой-либо поток.
В силу своей простоты и преимуществ в отношении производительности критические секции являются предпочтительным механизмом синхронизации, если из возможностей достаточно для того, чтобы удовлетворить требования, предъявляемые решаемой задачей.
Однако критическая секция имеет один существенный недостаток: она не может быть использована для синхронизации потоков принадлежащих различным процессам.
Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:
Источник: studopedia.ru
16Критическая секция.
Критическая секция — часть программы, в которой есть обращение к совместно используемым данным. При нахождении в критической секции двух (или более) процессов, возникает состояние «гонки» («состязания»). Для избежания данной ситуации необходимо выполнение четырех условий: ♦Два процесса не должны одновременно находиться в критических областях. ♦В программе не должно быть предположений о скорости или количестве процессоров. ♦Процесс, находящийся вне критической области, не может блокировать другие процессы.
♦Невозможна ситуация, в которой процесс вечно ждет попадания в критическую область.
Критическая секция (англ. critical section) — объект синхронизации потоков, позволяющий предотвратить одновременное выполнение некоторого набора операций (обычно связанных с доступом к данным) несколькими потоками. Критическая секция выполняет те же задачи, что и мьютекс.
Между мьютексом и критической секцией есть терминологические различия, так процедура, аналогичная захвату мьютекса, называется входом в критическую секцию, снятию блокировки мьютекса — выходом из критической секции. Процедура входа и выхода из критических секций обычно занимает меньшее время, нежели аналогичные операции мьютекса, что связано с отсутствием необходимости обращаться к ядру ОС.
В операционных системах семейства Microsoft Windows разница между мьютексом и критической секцией в том, что мьютекс является объектом ядра и может быть использован несколькими процессами одновременно, критическая секция же принадлежит процессу и служит для синхронизации только его потоков.
Критические секции Windows имеют оптимизацию, заключающуюся в использовании атомарно изменяемой переменной наряду с объектом «событие синхронизации» ядра. Захват критической секции означает атомарное увеличение переменной на 1. Переход к ожиданию на событии ядра осуществляется только в случае, если значение переменной до захвата было уже больше 1, то есть происходит реальное «соревнование» двух или более потоков за ресурс.
Таким образом, при отсутствии соревнования захват/освобождение критической секции обходятся без обращений к ядру.
Кроме того, захват уже занятой критической секции до обращения к ядру какое-то небольшое время (англ. spin count) ждёт в цикле опроса переменной, и, если переменная становится равной 0, то захват происходит без обращений к ядру.
Сходный объект в ядре Windows называется FAST_MUTEX (ExAcquire/ReleaseFastMutex). Он отличается от критической секции отсутствием поддержки рекурсивного повторного захвата тем же потоком.
17Программные алгоритмы организации взаимодействия процессов.
Требования, предъявляемые к алгоритмам
Организация взаимоисключения для критических участков, конечно, позволит избежать возникновения race condition, но не является достаточной для правильной и эффективной параллельной работы кооперативных процессов. Сформулируем пять условий, которые должны выполняться для хорошего программного алгоритма организации взаимодействия процессов, имеющих критические участки, если они могут проходить их в произвольном порядке.♦Задача должна быть решена чисто программным способом на обычной машине, не имеющей специальных команд взаимоисключения. При этом предполагается, что основные инструкции языка программирования (такие примитивные инструкции, как load, store, test ) являются атомарными операциями.♦Не должно существовать никаких предположений об относительных скоростях выполняющихся процессов или числе процессоров, на которых они исполняются.♦Если процесс Pi исполняется в своем критическом участке, то не существует никаких других процессов, которые исполняются в соответствующих критических секциях. Это условие получило название условия взаимоисключения (mutual exclusion).
♦Процессы, которые находятся вне своих критических участков и не собираются входить в них, не могут препятствовать другим процессам входить в их собственные критические участки. Если нет процессов в критических секциях и имеются процессы, желающие войти в них, то только те процессы, которые не исполняются в remainder section, должны принимать решение о том, какой процесс войдет в свою критическую секцию. Такое решение не должно приниматься бесконечно долго. Это условие получило название условия прогресса (progress) .
♦Не должно возникать неограниченно долгого ожидания для входа одного из процессов в свой критический участок. От того момента, когда процесс запросил разрешение на вход в критическую секцию, и до того момента, когда он это разрешение получил, другие процессы могут пройти через свои критические участки лишь ограниченное число раз. Это условие получило название условия ограниченного ожидания (bound waiting) .
Надо заметить, что описание соответствующего алгоритма в нашем случае означает описание способа организации пролога и эпилога для критической секции.
Запрет прерываний
Наиболее простым решением поставленной задачи является следующая организация пролога и эпилога:
while (some condition)
запретить все прерывания
разрешить все прерывания
Поскольку выход процесса из состояния исполнение без его завершения осуществляется по прерыванию, внутри критической секции никто не может вмешаться в его работу. Однако такое решение может иметь далеко идущие последствия, поскольку позволяет процессу пользователя разрешать и запрещать прерывания во всей вычислительной системе. Допустим, что пользователь случайно или по злому умыслу запретил прерывания в системе и зациклил или завершил свой процесс. Без перезагрузки системы в такой ситуации не обойтись.
Источник: studfile.net