Студопедия

КАТЕГОРИИ:


Архитектура-(3434)Астрономия-(809)Биология-(7483)Биотехнологии-(1457)Военное дело-(14632)Высокие технологии-(1363)География-(913)Геология-(1438)Государство-(451)Демография-(1065)Дом-(47672)Журналистика и СМИ-(912)Изобретательство-(14524)Иностранные языки-(4268)Информатика-(17799)Искусство-(1338)История-(13644)Компьютеры-(11121)Косметика-(55)Кулинария-(373)Культура-(8427)Лингвистика-(374)Литература-(1642)Маркетинг-(23702)Математика-(16968)Машиностроение-(1700)Медицина-(12668)Менеджмент-(24684)Механика-(15423)Науковедение-(506)Образование-(11852)Охрана труда-(3308)Педагогика-(5571)Полиграфия-(1312)Политика-(7869)Право-(5454)Приборостроение-(1369)Программирование-(2801)Производство-(97182)Промышленность-(8706)Психология-(18388)Религия-(3217)Связь-(10668)Сельское хозяйство-(299)Социология-(6455)Спорт-(42831)Строительство-(4793)Торговля-(5050)Транспорт-(2929)Туризм-(1568)Физика-(3942)Философия-(17015)Финансы-(26596)Химия-(22929)Экология-(12095)Экономика-(9961)Электроника-(8441)Электротехника-(4623)Энергетика-(12629)Юриспруденция-(1492)Ядерная техника-(1748)

Критические секции




 

Критические секции – исторически первый способ обеспечения синхронизации. Предложены Эдсгером Дейкстрой в 1965 году. Критическая секция – последовательность операторов внутри процесса, содержащая протоколы входа и выхода, удовлетворяющая нескольким условиям:

  1. Взаимное исключение В любой момент только один процесс может выполнять свою критическую секцию.
  2. Отсутствие взаимной блокировки. Если несколько процессов пытаются войти в свои критические секции, хотя бы один сделает это
  3. Если процесс пытается войти в критич. секцию, а другие выполняют некритические секции, то ему разрешается вход
  4. Процесс, который пытается войти в крит. секцию когда-нибудь это сделает.

1-3 – свойства безопасности, 4 – живучести.

Критическая секция может быть реализована методом активной блокировки с помощью разделяемой логической переменной lock. Начальное значение – false

 

// вход

While lock do;

lock:=true;

//Крит. Секция

//выход

lock:=false;

 

Как добиться неделимости действий? В процессорах есть операция проверить и установить (test and set). Ее логика описывается следующим кодом

Function TS(var lock:Boolean):Boolean;

Var n:Boolean;

Begin

N:=lock; lock:=true; TS:=N;

End;

 

Тогда вход опишется так

While TS(Lock);

 

Если обозначить вход в Крит. cекцию CSEnter, а выход – CSExit, то задача взаимной блокировки решается следующим кодом

CSEnter;

S;

CSExit;

 

Задача условной синхронизации – <await (B) S;>

CSEnter;

While not(B) do

Begin

CSExit;

Delay; // задержка на случайное время

CSEnter;

End;

S;

CSExit;

Такая условная синхронизация применяется в сети Ethernet. Чтобы передать сообщение контроллер отправляет его в сеть и следит, не возникла ли коллизия. Если была коллизия, делается пауза случайной длины и повторяется передача. Интервал для длины паузы удваивается, если коллизии повторяются.

Решение с активной блокировкой не является справедливой стратегией: одни потоки могут часто входить в секцию, другие – никогда не попасть в нее. Дело в том, что порядок входа не определен.

Существуют решения задачи критической секции со справедливой стратегией планирования: алгоритм разрыва узла, алгоритм билета, алгоритм поликлиники.

 

Алгоритм разрыва узла (алгоритм Питерсона) обеспечивает поочередный вход двух процессов в критическую секцию. Заводятся две логические переменные in1, in2 – значение true, если соответствующий поток находится в критической секции. Также заводится переменная Last, которая показывает какой поток пытается последним зайти в критическую секцию. Если оба процесса пытаются войти в Крит. Секцию, то выполнение последнего потока приостанавливается.

 

var in1, in2: boolean;

Last: integer;

 

//процесс 1

While true do

Begin

Last:=1; in1:=true;

While in2 and (last = 1) do;

//кр. Секция

in1:=false;

//некр. Секция

end;

 

//процесс 2

While true do

Begin

Last:=2; in2:=true;

While in1 and (last = 2) do;

//кр. Секция

in2:=false;

//некр. Секция

end;

 

Для n процессов алгоритм разрыва узла достаточно сложен.

 

Алгоритм билета основан на том, что каждый процесс, который пытается войти в кс получает номер, который больше номера любого из ранее вошедших. Процесс ждет, пока не будут выполнены кс процессов, имеющих меньшие номера.

 

Логика

 

Const number:integer =1; next:integer=1;

Var turn: array[1..n] of integer;

 

// процесс i

While true do

Begin

<turn[i]:=number; inc(number); >

//Здесь можно использовать обычный алгоритм CS (блокировка)

while turn[i]<>next do;

кр секция

inc(next)

некр секция

end;

 

Недостаток критических секций с активным ожиданием – сложность, отсутствие грани между переменными синхронизации и другими переменными, неэффективность (ожидающие процессы постоянно проверяют переменные, что занимает время процессора).

 

Гораздо лучше, если критические секции реализуются на уровне ОС. В Windows 32 такая реализация имеется.

 

Для работы с критическими разделами используются следующие функции:

VOID InitializeCriticalSection(LPCRITICAL_SECTION lpCriticalSection) - инициализация синхронизатора типа критический раздел.

lpCriticalSection - указатель на переменную типа CRITICAL_SECTION. Тип данных CRITICAL_SECTION является структурой, ее поля используются только Windows.

VOID EnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection) - запрос на вход в критический раздел с ожиданием.

BOOL TryCriticalSection(LPCRITICAL_SECTION lpCriticalSection) - запрос на вход в критический раздел без ожидания (с возвратом логического значения – вошли или нет).

VOID LeaveCriticalSection (LPCRITICAL_SECTION lpCriticalSection) - выход из критического раздела.

VOID DeleteCriticalSection(LPCRITICAL_SECTION lpCriticalSection) - удаление критического раздела (обычно при выходе из программы).

В DELPHI критические секции описаны в модуле Windows. Для описания переменной типа критическая секция нужно использовать тип TRTLCriticalSection

Итак, для создания критического раздела необходимо инициализировать структуру CRITICAL_SECTION. Создав объект CRITICAL_SECTION, мы можем работать с ним, т.е. можем обозначить код, доступ к которому для одновременно выполняющихся задач требуется синхронизировать. Если один поток вошел в критический раздел, то следующий поток, вызывая функцию EnterCriticalSection с тем же самым объектом типа CRITICAL_SECTION, будет задержан внутри функции. Возврат произойдет только после того, как первый поток покинет критический раздел, вызвав функцию LeaveCriticalSection. В этот момент второй поток, задержанный в функции EnterCriticalSection, станет владельцем критического раздела, и его выполнение будет возобновлено.

 
Когда объект критический раздел больше не нужен программе, его можно удалить с помощью функции DeleteCriticalSection. Это приведет к освобождению всех ресурсов системы, задействованных для поддержки объекта критический раздел.

Заметим, что возможно определение нескольких объектов типа критический раздел. Если в программе имеется четыре потока, и два из них разделяют одни данные, а два других - другие, то потребуется два объекта типа критический раздел.

Рассмотрим такой пример. Мы хотим записывать и считывать значения из некоего глобального массива mas. Причем запись и считывание должны производиться двумя разными потоками. Вполне естественно, что лучше, если эти действия не будут выполняться одновременно. Поэтому введем ограничение на доступ к массиву.

 

Листинг 1. Ограничение доступа к массиву с использованием критических разделов

 

// Массив значений.

Var mas: array [1..1000] of integer;

// Критическая секция, регулирующая доступ к массиву

Var CritSec: TRTLCriticalSection;

// Инициализируем критический раздел

InitializeCriticalSection(CritSect);

// Запускаем потоки

Thread1.Resume;

Thread2.Resume;

DeleteCriticalSection(CritSec);

 

// Первый поток: запись в массив данных

procedure Thread1.Execute;

Var i: integer;

Begin // Запись значения в массив

// Запрос на вход в критический раздел

EnterCriticalSection(CritSec);

// Выполнение кода в критическом разделе

for i:= 1 to 1000 do mas[i]:= i;

// Выход из критического раздела:

// освобождаем критический раздел для доступа

// к нему других задач

LeaveCriticalSection(CritSec);

End;

// Второй поток: считывание данных из массива

procedure Thread2.Execute;

Var i: integer;

Begin // Считывание значений из массива

// Запрос на вход в критический раздел

EnterCriticalSection(CritSec);

// Выполнение кода в критическом разделе

for i:= 1 to 1000 do j:=mas[i];

// Выход из критического раздела:

// освобождаем критический раздел для доступа

// к нему других задач

LeaveCriticalSection(CritSec);

End;

И хотя приведенный нами пример подобного ограничения (см. листинг 1) чрезвычайно упрощен, он хорошо иллюстрирует работу синхронизатора типа критический раздел: пока один поток "владеет" массивом, другой доступа к нему не имеет.

Вход в критическую секцию уже занявшим его потоком возможен любое количество раз (столько же раз необходимо выполнить операцию выхода).

 

Для упрощения работы с критическими секциями в DELPHI и C++Builder есть специальный класс TCriticalSection.

У него имеется конструктор Create без параметров, методы Acquire и Enter для входа в секцию, методы Release и Leave для выхода из секции. Для удаления объекта используется Free. Перепишем наш пример.

 




Поделиться с друзьями:


Дата добавления: 2014-01-07; Просмотров: 1049; Нарушение авторских прав?; Мы поможем в написании вашей работы!


Нам важно ваше мнение! Был ли полезен опубликованный материал? Да | Нет



studopedia.su - Студопедия (2013 - 2024) год. Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав! Последнее добавление




Генерация страницы за: 0.034 сек.