Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Учебное пособие 300072.doc
Скачиваний:
2
Добавлен:
30.04.2022
Размер:
297.98 Кб
Скачать

Семафоры

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

function CreateSemaphore (lpSemaphoreAttributes: PSecurityAttributes; lInitialCount, lMaximumCount: Longint; lpName: PChar); THandle; stdcall;

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

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

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

Параметр lpName содержит имя семафора. Поведение этого параметра аналогично поведению одноименного параметра функции CreateMutex ().

Задание 4. Составить программу для задачи Задания 1. Выполнение инициализаций реализовать в двух отдельных потоках с использованием метода семафоров для выполнения синхронизации потоков.

Один из способов решения данной задачи следующий.

Поскольку мы разрешаем только одному потоку проходить через участок кода синхронизации, максимальное значение счетчика для семафора в этом случае равно 1.

Функции ReleaseSemaphore () используется для увеличения значения счетчика семафора. Oбpaтитe внимание, что эта функция имеет больше параметров, чем ее коллега ReleaseMutex (). Объявление функции ReleaseSemaphore () выглядит следующим образом:

function ReleaseSemaphore(hSemaphore: THandle; lReleaseCount: Longint; lpPreviousCount: Pointer): BOOL; stdcall;

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

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

Пример 4 модуля демонстрации метода семафоров для выполнения синхронизации потоков при решении проблемы инициализации массива.

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

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;

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.ButtonlClick(Sender: TObject);

begin

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

TFooThread.Create(False); // Создаем потоки

TFooThread.Create(False) ;

end;

end.