Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Martin Harvey - Threads.doc
Скачиваний:
20
Добавлен:
01.03.2025
Размер:
1 Мб
Скачать
☆

Простой mrews с использованием событий.

Управляющие структуры, требуемые для имитации события с использованием семафоров, подобны структурам, использованным в простом синхронизаторе. Таким образом, имеет смысл попытаться создать синхронизатор, используя события вместо семафоров. Это не слишком трудно:

unit EventSync;

{ Martin Harvey 5/6/2000 }

Interface

uses Windows;

type

TEventSynchronizer = class(TObject)

Private

FDataLock, FWriteLock: TRTLCriticalSection;

FReaders, FWriters: integer;

FNoReaders, FNoWriters: THandle;

Public

constructor Create;

destructor Destroy; override;

procedure StartRead;

procedure StartWrite;

procedure EndRead;

procedure EndWrite;

end;

implementation

constructor TEventSynchronizer.Create;

begin

inherited Create;

InitializeCriticalSection(FDataLock);

InitializeCriticalSection(FWriteLock);

FNoReaders := CreateEvent(nil, true, true, nil);

FNoWriters := CreateEvent(nil, true, true, nil);

end;

destructor TEventSynchronizer.Destroy;

begin

DeleteCriticalSection(FDataLock);

DeleteCriticalSection(FWriteLock);

CloseHandle(FNoReaders);

CloseHandle(FNoWriters);

inherited Destroy;

end;

procedure TEventSynchronizer.StartRead;

var

Block: boolean;

Begin

EnterCriticalSection(FDatalock);

if FReaders = 0 then

ResetEvent(FNoReaders);

Inc(FReaders);

Block := FWriters > 0;

LeaveCriticalSection(FDataLock);

if Block then

WaitFor

end;

Глава 12. Еще о возможностях синхронизации в Win32. Повышенная эффективность с помощью операций взаимоблокировки (interlocked)

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

Interlocked (взаимосвязанные, взаимоблокировочные) операции первоначально были задуманы как механизм синхронизации низкого уровня для симметричных многопроцессорных систем с разделяемой памятью. Во многопроцессорных системах общая память представляет собой чрезвычайно эффективный путь обмена данными между процессами и потоками. Необходимо обеспечить методы решения проблем атомарности, когда два или более процессоров пытаются использовать один и тот же участок памяти. Почти все современные процессоры поддерживают соответствующие операции взаимоблокировки. Эти такие операции, посредством которыхо процессор может прочитать значение из памяти, модифицировать его, а затем записать атомарно, причем гарантируется, что другие процессоры не получит доступ к тому же участку памяти, а процессор, выполняющий операцию, не будет прерван. Win32 обеспечивает следующие операции взаимоблокировки:

  • InterlockedCompareExchange (только Win NT/2K).

  • InterlockedDecrement.

  • InterlockedExchange.

  • InterlockedExchangeAdd (только Win NT/2K).

  • InterlockedIncrement.

Так почему бы не использовать такие операции всегда? Хорошим примером может служить замок (spin lock). Допустим, есть желание создать что-то подобное критической секции. Однако в критической секции может быть очень мало кода, а доступ к нему нужен очень часто. В таких случаях, полноценный объект синхронизации может оказаться излишним. Spin lock (замок) позволяет нам добиться аналогичного результата, и работает примерно так

Var

Lock:integer;

{Lock Initialization}

Lock := -1;

{Entering the spin lock}

while InterlockedIncrement(Lock)>0 do

begin

Dec(Lock);

Sleep(0);

end;

{Leaving the spin lock}

Dec(Lock);

Поток захватывает замок, если при выполнении приращения InterlockedIncrement значение Lock будет нулевым. Если эта величина больше нуля, то замок захвачен другим потоком, и требуется новая попытка. Вызов Sleep нужен, чтобы один поток не ждал впустую слишком долго, пока поток с более низким приоритетом удерживает замок. Для простых планировщиков, если приоритеты потоков равны, то вызов Sleep может и не потребоваться. Операция блокировки необходима, поскольку если поток выполняет чтение значения из памяти, увеличение его, сравнение, а затем записывает назад, то два потока могут захватить замок одновременно.

Излишние действия почти исключены, поскольку лишь несколько инструкций CPU требуется для входа и выхода из замка, а потоку не приходится ждать. Если потокам нужно ожидать существенное время, то процессор работает впустую, так что это подходит только для создания небольших критических секций. Замки полезны для реализации критических секций, которые сами являются частью структур синхронизации. Коллективные данные внутри примитивов синхронизации или планировщиков часто защищены блокировкой подобного типа: часто это необходимо, поскольку примитивы синхронизации уровня OС не могут быть использованы, чтобы осуществлять примитивы синхронизации уровня OС. У такой блокировки имеются те же проблемы с конкуренцией потоков, как и у мьютексов, но отличие состоит в том, что зацикливание происходит не путем замораживания (deadlock), а динамически (livelock). Это несколько худшая ситуация, потому что хотя "блокированные" потоки не выполняют полезного кода, они работают в бесконечном цикле, расходуя время процессора и понижая производительность всей системы. Замки нельзя использовать как семафоры, чтобы "приостанавливать" потоки.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]