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

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

   
Семафор (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 может принимать одно из следующих значений:

  • SEMAPHORE_ALL_ACCESS - поток получает все права на семафор;
  • SEMAPHORE_MODIFY_STATE - поток может увеличивать счетчик семафора функцией ReleaseSemaphore;
  • SYNCHRONIZE (только для Windows NT) - поток может использовать семафор в функциях ожидания.

   
Для увеличения счетчика семафора используется функция 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;

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



Вы можете оставить комментарий, или Трекбэк с вашего сайта.

Оставить комментарий