Мои Конспекты
Главная | Обратная связь


Автомобили
Астрономия
Биология
География
Дом и сад
Другие языки
Другое
Информатика
История
Культура
Литература
Логика
Математика
Медицина
Металлургия
Механика
Образование
Охрана труда
Педагогика
Политика
Право
Психология
Религия
Риторика
Социология
Спорт
Строительство
Технология
Туризм
Физика
Философия
Финансы
Химия
Черчение
Экология
Экономика
Электроника

Этот код гарантированно работает только с одним потоком чтения, с одним потоком записи и одним управляющим. Почему?



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

procedureTBoundedBuffer.ResetState;

{ Closes handles and deallocates memory.

Note that this must unblock threads in such a manner that

they quit cleanly }

Var

SemCount: integer;

Begin

IfFBufInit then

Begin

WaitForSingleObject(FCriticalMutex, DefaultWaitTime);

FBufInit := false;

FBufSize := 0;

FreeMem(FBuf);

Repeat

ReleaseSemaphore(FEntriesFree, 1, @SemCount);

untilSemCount = 0;

Repeat

ReleaseSemaphore(FEntriesUsed, 1, @SemCount);

untilSemCount = 0;

CloseHandle(FEntriesFree);

CloseHandle(FEntriesUsed);

CloseHandle(FCriticalMutex);

end;

end;

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

Доступ к дескрипторам синхронизации должен быть синхронизован!

Следующая возможность - обнулить дескриптор семафора прямо перед "отключением" его, сделав нечто подобное

procedureTBoundedBuffer.ResetState;

 

{ Closes handles and deallocates memory.

Note that this must unblock threads in such a manner that

they quit cleanly }

Var

SemCount: integer;

LocalHandle: THandle;

 

Begin

IfFBufInit then

Begin

WaitForSingleObject(FCriticalMutex, DefaultWaitTime);

FBufInit := false;

FBufSize := 0;

FreeMem(FBuf);

LocalHandle := FEntriesFree;

FEntriesFree := 0;

Repeat

ReleaseSemaphore(LocalHandle, 1, @SemCount);

untilSemCount = 0;

CloseHandle(LocalHandle);

LocalHandle := FEntriesUsed;

FEntriesUsed := 0;

Repeat

ReleaseSemaphore(LocalHandle, 1, @SemCount);

untilSemCount = 0;

CloseHandle(LocalHandle);

CloseHandle(FCriticalMutex);

end;

end;

(Автор будет честен и признает, что это жалкое неполноценное решение пришло ему в голову). Однако это ничем не лучше. Вместо проблемы тупика мы получим конфликт потоков непростого типа. Этот конфликт представляет собой запись после чтения для самого дескриптора семафора! Да... Вы должны синхронизировать даже ваши объекты синхронизации! Вот что может случиться: рабочий поток читает значение дескриптора мьютекса из буферного объекта и приостанавливается, ожидая; в этот момент поток очистки, уничтожающий буфер, освобождает мьютекс необходимое число раз, и именно в этот момент рабочий поток возобновляется и обращается к мьютексу, который, как мы считаем, только что был уничтожен! Интервал, в котором это может случиться, очень небольшой, но тем не менее, это решение неприемлемо.