- •Гоувпо «Воронежский государственный технический университет»
- •Методические указания
- •Требования к выполнению и оформлению лабораторных работ
- •Практическое применение потоков
- •Основы работы объекта tThread
- •Завершение работы потока
- •Преимущества однопоточного интерфейса пользователя
- •Метод Synchronize
- •Хранение локальных данных потоков
- •Использование объекта tThread для хранения данных
- •Threadvar: хранение локальных данных потоков с помощью интерфейса api
- •Синхронизация потоков
- •Критические разделы
- •Мьютексы
- •Семафоры
- •Библиографический список
- •Содержание
- •394026 Воронеж, Московский просп., 14
Семафоры
Существует еще один метод синхронизации потоков, в котором используются семафорные объекты 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.