Главная » Delphi » Семафоры

0

Существует еще один метод синхронизации потоков, в котором используются объ екты  семафоров (semaphore) API. В семафорах применен принцип действия мьютек сов, но с одной  существенной деталью.  В них заложена возможность подсчета ресур сов, что позволяет заранее определенному количеству потоков одновременно войти в синхронизируемый блок кода. Для создания семафора используется функция API CreateSemaphore(), которая объявляется следующим образом:

function CreateSemaphore(lpSemaphoreAttributes: PSecurityAttributes; lInitialCount, lMaximumCount: Longint;

lpName: PChar): THandle; stdcall;

Как  и  у функции  CreateMutex(), первым параметром,  передаваемым функ ции  CreateSemaphore(), является указатель  на запись  TSecurityAttributes, причем значение Nil соответствует согласию на использование стандартных атрибу тов защиты.

Параметр lInitialCount представляет собой  начальное значение счетчика объекта семафора. Это число  может  находиться в диапазоне от 0 до значения lMaximumCount. Семафор доступен, если значение данного параметра больше нуля. Когда поток  вызывает функцию WaitForSingleObject() или любую другую, ей подобную, значение счетчика семафора уменьшается на единицу.  И наоборот, при вызове потоком функции Release- Semaphore() значение счетчика семафора увеличивается на единицу.

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

Параметр lpName содержит имя семафора. Поведение этого параметра аналогич

но поведению одноименного параметра функции CreateMutex().

В  листинге 5.7  продемонстрировано  использование  семафоров  для  синхрониза

ции потоков при инициализации массива.

Листинг 5.7. Использование семафоров для синхронизации потоков

unit Main;

interface uses

Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls;

type

TMainForm = class(TForm)

Button1: TButton;

ListBox1: TListBox;

procedure Button1Click(Sender: TObject);

private

procedure ThreadsDone(Sender: TObject);

end;

TFooThread = class(TThread)protected

procedure Execute; override;

end;

var

MainForm: TMainForm;

implementation

{$R *.DFM}

const

MaxSize =128;

var

NextNumber: Integer = 0;

DoneFlags: Integer = 0;

GlobalArray: array[1..MaxSize] of Integer;

hSem: THandle = 0;

function GetNextNumber: Integer;

begin

Result := NextNumber;      // возвращает глобальную переменную

Inc(NextNumber);           // инкремент глобальной переменной

end;

procedure TFooThread.Execute;

var

i: Integer;

WaitReturn: DWORD;

begin

OnTerminate := MainForm.ThreadsDone;

WaitReturn := WaitForSingleObject(hSem, INFINITE);

if WaitReturn = WAIT_OBJECT_0 then begin

for i := 1 to MaxSize do begin

GlobalArray[i] := GetNextNumber;         // запись элемента массива

Sleep(5);                                // смена потока

end;

end;

ReleaseSemaphore(hSem, 1, nil);

end;

procedure TMainForm.ThreadsDone(Sender: TObject);

var

i: Integer;

begin

Inc(DoneFlags);

if DoneFlags = 2 then begin       // если оба потока завершены

for i := 1 to MaxSize do

{ заполнить список элементами массива }

Listbox1.Items.Add(IntToStr(GlobalArray[i]));

CloseHandle(hSem);

end;end;

procedure TMainForm.Button1Click(Sender: TObject);

begin

hSem := CreateSemaphore(nil, 1, 1, nil);

TFooThread.Create(False);      // создать потоки

TFooThread.Create(False);

end;

end.Поскольку проход через блок кода синхронизации разрешен только одному пото

ку, максимальное значение счетчика семафора равно  1.

Функция  ReleaseSemaphore() используется для увеличения значения счетчика

семафора.  Обратите  внимание,  эта   функция   имеет   больше   параметров,  чем   ее

“коллега”  ReleaseMutex(). Объявление функции  ReleaseSemaphore() выглядит следующим образом:

function ReleaseSemaphore(hSemaphore: THandle;

lReleaseCount: Longint;

lpPreviousCount: Pointer): BOOL; stdcall;

С помощью параметра lReleaseCount можно  задать  число, на которое будет умень шено значение счетчика семафора. При этом старое значение счетчика будет сохранено в переменной типа  Longint, на которую  указывает  параметр lpPreviousCount, если его значение не равно  Nil. Скрытый смысл данной возможности заключается в том, что се мафор никогда  не принадлежит ни одному отдельному потоку. Предположим, что макси мальное значение счетчика семафора было равно  10 и десять  потоков вызвали функцию WaitForSingleObject(). В результате счетчик потоков сбрасывается до нуля и тем са мым семафор переводится в недоступное состояние. После  этого  достаточно одному из потоков вызвать функцию ReleaseSemaphore() и в качестве параметра lReleaseCount передать число 10, как семафор не просто будет снова пропускать потоки, т.е. станет дос тупным,  но и увеличит значение своего  счетчика до прежнего числа — до 10. Такое  мощ ное средство может  привести к ошибкам, которые будет трудно обнаружить, поэтому  его следует использовать с большой осторожностью.

Для освобождения дескриптора семафора, выделенного ему с помощью функции

CreateSemaphore(), не забудьте вызвать функцию CloseHandle().

Источник: Тейксейра, Стив, Пачеко, Ксавье.   Borland Delphi 6. Руководство разработчика. : Пер.  с англ. — М. : Издательский дом “Вильямс”, 2002. —  1120 с. : ил. — Парал. тит. англ.

По теме:

  • Комментарии