На этом шаге мы дадим определение семафора и рассмотрим области его использования.
Семафор (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;
Со следующего шага мы начнем рассматривать дополнительные механизмы синхронизации.