Шаг 296.
Модели потоков. Объекты синхронизации. Семафоры

    На этом шаге мы дадим определение семафора и рассмотрим области его использования.

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

function CreateSemaphore(
  lpSemaphoreAttributes: PSecurityAttributes; 
    // Адрес структуры TSecurityAttributes
  lInitialCount,
    // Начальное значение счетчика
  lMaximumCount: Longint;
    // Максимальное значение счетчика
  lpName: PChar
    // Имя объекта 
): THandle; stdcall;

    Функция возвращает либо идентификатор созданного семафора, либо 0, если создать объект не удалось.

    Параметр lMaximumCount задает максимальное значение счетчика семафора, параметр lInitialCount - начальное значение в диапазоне от 0 до lMaximumCount. Параметр lpName задает имя семафора. Если в системе уже есть семафор с таким именем, то новый не создается, а возвращается идентификатор существующего семафора. В случае если семафор используется внутри одного процесса, можно создать его без имени, передав в качестве параметра lpName значение nil. Имя семафора не должно совпадать с именем уже существующего объекта типа событие, мыотекс, задание, таймер ожидания или файл, отображенный на память.

    Идентификатор ранее созданного семафора может быть также получен с помощью функции:

function OpenSemaphore(
  dwDesiredAccess: DWORD;
    // Задает права доступа к объекту
  bInheritHandle: BOOL;
    // Задает, может ли объект наследоваться дочерними процессами
  lpName: PChar
    // Имя объекта 
): THandle; stdcall;

    Параметр dwDesiredAccess может принимать одно из следующих значений:

    Для увеличения счетчика семафора используется функция ReleaseSemaphore:

function ReleaseSemaphore(
  hSemaphore: THandle;
    // Идентификатор семафора
  lReleaseCount: Longint;
    // Счетчик будет увеличен на эту величину
  lpPreviousCount: Pointer
    // Адрес 32-битной переменной с предыдущим значением счетчика 
): BOOL; stdcall;

    Если значение счетчика после выполнения функции превысит заданный для него функцией CreateSemaphore максимум, то ReleaseSemaphore возвращает False и значение семафора не изменяется. В качестве параметра lpPreviousCount можно передать nil, если это значение нам не нужно.

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

unit LimitedThread;
interface

uses Classes;

type
  TLimitedThread = class(TThread)
    procedure Execute; override;
  end;
 
implementation

uses Windows;

const
  MAX_THREAD_COUNT = 10;

var
  Semaphore: THandle;

procedure TLimitedThread.Execute;
begin
  // Уменьшаем счетчик семафора. 
  // Если к этому моменту уже запущено
  // MAX_THREAD_COUNT потоков (счетчик равен 0)
  // и семафор находится в несигнальном состоянии, 
  // поток будет заморожен до завершения 
  // одного из запущенных ранее потоков.
  WaitForSingleObject(Semaphore, INFINITE);
  
  // Здесь располагается код, отвечающий за функциональность 
  // потока, например код загрузки файла
  .   .   .
  // Поток завершил работу, увеличиваем 
  // счетчик семафора и позволяем
  // начать обработку другим потокам
  ReleaseSemaphore(Semaphore, 1, nil);
end;

initialization
  // Создаем семафор при старте программы
  Semaphore := CreateSemaphore(nil, MAX_THREAD_COUNT, 
    MAX_THREAD_COUNT, nil);

Finalization
  // Уничтожаем семафор по завершении программы
  CloseHandle(Semaphore);
end;

    Со следующего шага мы начнем рассматривать дополнительные механизмы синхронизации.




Предыдущий шаг Содержание Следующий шаг