Чтение онлайн

на главную - закладки

Жанры

Фундаментальные алгоритмы и структуры данных в Delphi

Бакнелл Джулиан М.

Шрифт:

Поскольку одновременно только один поток записи может быть активным, может показаться целесообразным поместить объект синхронизации, который ставит потоки записи в очередь, также в критический раздел, поскольку этот раздел может принадлежать только одному потоку. Однако на практике проще воспользоваться семафором. Причина этого проста: в действительности не требуется вызов объекта синхронизации, поскольку не существует подходящего места для его освобождения. Действительно, вы убедитесь, что придется дожидаться семафора в одном потоке и освобождать его в другом. Такой подход невозможен при использовании критического раздела: поток, обращающийся к критическому

разделу, владеет им.

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

в данной реализации поток может находиться в состоянии, в котором он еще не вызвал подпрограмму WaitFor).

Код интерфейса создаваемого нами класса синхронизации TtdReadWriteSync приведен в листинге 12.1. Он содержит ряд приватных полей, которые будут использоваться в четырех основных методах.

Листинг 12.1. Интерфейс класса TtdReadWriteSync

type

TtdReadWriteSync = class private

FActiveReaders : integer;

FActiveWriter : boolean;

FBlockedReaders : THandle;

{семафор}

FBlockedWriters : THandle;

{семафор}

FController : TRTLCriticalSection;

FWaitingReaders : integer;

FWaitingWriters : integer;

protected

public

constructor Create;

destructor Destroy; override;

procedure StartReading;

procedure StartWriting;

procedure StopReading;

procedure StopWriting;

end;

Приватное поле FBlockedReaders семафора предназначено для ожидающих потоков считывания, а поле FBlockedWriters - для ожидающих потоков записи. Поле FController - основной компонент, обеспечивающий последовательный доступ к объектам (к сожалению, применение подобного механизма последовательной обработки необходимо для обеспечения того, чтобы каждый поток получал целостное и неискаженное изображение всего класса).

Код метода StartReading приведен в листинге 12.2.

Листинг 12.2. Метод StartReading

procedure TtdReadWriteSync.StartReading;

var

HaveToWait : boolean;

begin

{перехватить управление критическим разделом}

EnterCriticalSection(FController);

{если существует выполняющийся поток записи или хотя бы один ожидающий своей очереди поток записи, метод добавляет себя в качестве ожидающего метода записи, обеспечивая переход в состояние ожидания}

if FActiveWriter or (FWaitingWriters <> 0) then begin

inc(FWaitingReaders);

HaveToWait :=true;

end

{в противном случае он добавляет себя в качестве еще одного выполняющегося потока считывания и обеспечивает отсутствие состояния ожидания}

else begin

inc(FActiveReaders);

HaveToWait := false;

end;

{освободить управление критическим разделом}

LeaveCriticalSection(FController);

{при необходимости ожидания нужно выполнить следующее}

if HaveToWait then

WaitForSingleObject(FBlockedReaders, INFINITE);

end;

Прежде всего, мы перехватываем управление критическим разделом. После этого можно осуществлять управление значениями

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

Рассмотрим метод StopReading, код которого приведен в листинге 12.3.

Листинг 12.3. Метод StopReading

procedure TtdReadWriteSync.StopReading;

begin

{перехватить управление критическим разделом}

EnterCriticalSection(FController);

{считывание завершено}

dec (FActiveReaders);

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

if (FActiveReaders = 0) and (FWaitingWriters <> 0) then begin

dec(FWaitingWriters);

FActiveWriter :=true;

ReleaseSemaphore(FBlockedWriters, 1, nil);

end;

{освободить управление критическим разделом}

LeaveCriticalSection(FController);

end;

Как обычно, прежде всего, мы перехватываем управление критическим разделом. Этот поток стремится прекратить свои действия по считыванию, поэтому он уменьшает значение счетчика выполняющихся потоков считывания. Если результирующее значение не равно нулю, это свидетельствует о наличии других активных потоков считывания. Поэтому метод просто освобождает управление критическим разделом и осуществляет выход. Однако если этот поток был последним активным потоком считывания, теперь значение счетчика равно нулю и нужно предоставить свободу действий ожидающему потоку записи (если таковой существует). Для этого метод освобождает семафор заблокированных потоков записи. Иначе говоря, метод увеличивает значение счетчика на единицу, в результате чего система предоставит свободу действий одному, и только одному, заблокированному потоку записи, после чего немедленно снова уменьшит значение счетчика до нуля, обеспечивая блокировку всех остальных потоков записи. Однако непосредственно перед тем метод StopReading уменьшает значение счетчика ожидающих потоков записи и увеличивает значение счетчика выполняющихся потоков записи. Общий результат выполнения этого кода состоит в том, что поток записи освобождается, а значения двух счетчиков потоков записи обновляются.

Перейдем к рассмотрению метода StartWriting, код которого приведен в листинге 12.4.

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

Листинг 12.4. Метод StartWriting

procedure TtdReadWriteSync.StartWriting;

Поделиться:
Популярные книги

Лорд Системы 12

Токсик Саша
12. Лорд Системы
Фантастика:
фэнтези
попаданцы
рпг
5.00
рейтинг книги
Лорд Системы 12

Младший научный сотрудник 2

Тамбовский Сергей
2. МНС
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Младший научный сотрудник 2

#Бояръ-Аниме. Газлайтер. Том 11

Володин Григорий Григорьевич
11. История Телепата
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
#Бояръ-Аниме. Газлайтер. Том 11

Цеховик. Книга 2. Движение к цели

Ромов Дмитрий
2. Цеховик
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Цеховик. Книга 2. Движение к цели

Неестественный отбор.Трилогия

Грант Эдгар
Неестественный отбор
Детективы:
триллеры
6.40
рейтинг книги
Неестественный отбор.Трилогия

Попаданка в семье драконов

Свадьбина Любовь
Попаданка в академии драконов
Любовные романы:
любовно-фантастические романы
7.37
рейтинг книги
Попаданка в семье драконов

Звезда сомнительного счастья

Шах Ольга
Фантастика:
фэнтези
6.00
рейтинг книги
Звезда сомнительного счастья

Беглец

Кораблев Родион
15. Другая сторона
Фантастика:
боевая фантастика
попаданцы
рпг
5.00
рейтинг книги
Беглец

Я – Орк

Лисицин Евгений
1. Я — Орк
Фантастика:
юмористическая фантастика
попаданцы
аниме
5.00
рейтинг книги
Я – Орк

Смерть может танцевать 2

Вальтер Макс
2. Безликий
Фантастика:
героическая фантастика
альтернативная история
6.14
рейтинг книги
Смерть может танцевать 2

Идеальный мир для Лекаря 18

Сапфир Олег
18. Лекарь
Фантастика:
юмористическое фэнтези
аниме
5.00
рейтинг книги
Идеальный мир для Лекаря 18

Жандарм

Семин Никита
1. Жандарм
Фантастика:
попаданцы
альтернативная история
аниме
4.11
рейтинг книги
Жандарм

Путь Шедара

Кораблев Родион
4. Другая сторона
Фантастика:
боевая фантастика
6.83
рейтинг книги
Путь Шедара

Гром над Империей. Часть 1

Машуков Тимур
5. Гром над миром
Фантастика:
фэнтези
5.20
рейтинг книги
Гром над Империей. Часть 1