- •Глава 2. Синхронизация задач с использованием api-функций и механизмов ядра.
- •§0. Объекты ядра. Основы.
- •Учет пользователей объектов ядра
- •Процесс и поток: краткая характеристика
- •Создание объекта ядра
- •Закрытие объекта ядра
- •Синхронизация объектов
- •Наследование описателей объекта
- •Именованные объекты
- •3. После этого вызова система проверяет, существует ли уже объект ядра с таким же именем. Если существует, то
- •Open-функции
- •Дублирование описателей объектов
- •1) Процесс-катализатор.
- •2) Процесс-источник.
- •3) Процесс-приемник.
- •Исходное состояние
- •1 0XF0000000 (неприм.) (неприм.)
- •1 0XF0000000 (неприм.) (неприм.)
- •Состояние после вызова DuplicateHandle
- •§1. Синхронизация задач с использованием функций ожидания.
- •Функция ожидания одного объекта
- •Функция ожидания нескольких объектов
- •Функция ожидания нескольких объектов и сообщений
- •Функция создания дочернего процесса
- •Функция завершения дочернего процесса из самого дочернего процесса
- •Функция завершения дочернего процесса из процесса родителя
- •Функция создания вторичного потока
- •Функция завершения дочернего потока из самого дочернего потока (only c)
- •Функция завершения дочернего потока из потока родителя
- •If (my_file.Is_open()) { /// Если удалось открыть
- •Функция создания вторичного потока _beginthread
- •Функция создания вторичного потока _beginthreadex
- •§2. Синхронизация задач с помощью объектов ядра «событие» (Event).
- •Функция создания события
- •Функция установки сигнального состояния события
- •Функция установки несигнального состояния события
- •Функция открытия существующего именованного объекта события
- •Дескриптор защиты (структура)
- •§3. Синхронизация задач с помощью объектов ядра «семафор» (Semaphore).
- •Функция создания семафора
- •Функция открытия семафора
- •Функция увеличения счетчика семафора на указанное количество
- •If (!ReleaseSemaphore( ghSemaphore, 1, null)) /// Если ошибка
- •§4. Синхронизация задач с помощью объектов ядра «мьютекс» (Mutex).
- •Функция создания мьютекса
- •Функция открытия существующего именованного объекта мьютекса
- •Функция освобождения владельца указанного объекта мьютекса
- •§5. Синхронизация задач с помощью объектов ядра «уведомление об изменении» (Change Notification).
- •Функция создания объекта ядра «уведомление об изменении»
- •Функция перезапуска объекта ядра «уведомление об изменении»
- •Функция остановки мониторинга дескриптора объекта ядра «уведомление об изменении»
- •§6. Синхронизация задач с помощью объектов ядра «таймер ожидания» (Waitable Timer).
- •Функция создания объекта ядра «таймер ожидания»
- •Функция активации объекта ядра «ожидаемый таймер»
- •Функция открытия объекта ядра «ожидаемый таймер»
- •1. Функцией CancelWaitableTimer().
- •2. Функцией SetWaitableTimer().
- •If (bSuccess) /// Если успешно, то
- •If (bSuccess) /// Если успешно, то
- •§7. Синхронизация задач с помощью объектов ядра «канал» (Pipe).
- •Анонимный канал
- •Создание анонимных каналов
- •Соединение клиентов с анонимным каналом
- •Обмен данными по анонимному каналу
- •Именованный канал
- •Функция создания именованного канала
- •Функция соединения сервера с клиентом
- •Отключение сервера от клиента
- •Функция ожидания операции именованного канала
- •Функция объединения функций именованного канала
- •Подключение клиента к серверу
Функция создания семафора
HANDLE CreateSemaphore(
IN LPSECURITY_ATTRIBUTES lpSemAttr,
IN LONG lInitCount,
IN LONG lMaxCount,
IN LPCSTR lpName
);
(1) lpSemAttr определяет указатель на структуру SECURITY_ATTRIBUTES. Если этот параметр равен NULL, дескриптор не может наследоваться дочерними процессами. Член этой структуры lpSecurityDescriptor определяет дескриптор безопасности для нового семафора. Если lpSemAttr имеет значение NULL, семафор получает дескриптор безопасности по умолчанию.
(2) lInitCount определяет начальное значение счетчика.
(3) lMaxCount определяет максимальное значение счетчика.
(4) lpName определяет имя семафора.
Возвращаемое значение:
Если функция завершается успешно, возвращаемое значение является дескриптором объекта семафора.
Если указанный объект семафора существовал до вызова функции, функция возвращает дескриптор существующего объекта, а параметры 2 и 3 игнорируются.
Если функция завершается ошибкой, возвращаемое значение равно NULL.
Функция открытия семафора
HANDLE OpenSemaphore(
IN DWORD dwDesiredAccess,
IN BOOL bInheritHandle,
IN LPCSTR lpName
);
Параметры и описание:
(1) dwDesiredAccess — определяет доступ к объекту. 3 варианта: SEMAPHORE_ALL_ACCESS (позволяет выполнять все действия по отношению к объекту семафора), SEMAPHORE_MODIFY_STATE (поток может использовать только функцию ReleaseSemaphore), SYNCHRONIZE (в потоке можно использовать события функций ожидания).
(2) bInheritHandle — определяет наследование. TRUE — дочерние процессы наследуют дескриптор. FALSE — не наследуют.
(3) lpName — определяет имя семафора.
Возвращаемое значение:
Если функция завершилась успешно, она возвращает дескриптор объекта семафора, в противном случае — возвращает NULL.
Функция увеличения счетчика семафора на указанное количество
BOOL ReleaseSemaphore(
IN HANDLE hSemaphore,
IN LONG lReleaseCount,
OUT LPLONG lpPrevCount
);
Параметры и описание:
(1) hSemaphore определяет дескриптор объекта семафора.
(2) lReleaseCount определяет величину, на которую увеличивается текущее число объектов семафора. Значение должно быть больше нуля.
(3) lpPrevCount определяет указатель на переменную для записи в неё предыдущего значения счетчика семафора. Можно указать NULL.
Возвращаемое значение:
Если функция завершается успешно, возвращаемое значение отлично от нуля (bool: true), иначе возвращаемое значение равно 0 (bool: false).
Пример.
/// S — handle семафора
long N = MaxCount;
if (ReleaseSemaphore(S, 1, &N))
WaitForSingleObject(S, 1000);
Если текущее значение счетчика меньше максимально возможного, то вызов функции ReleaseSemaphore сохраняет в N текущее значение счетчика, увеличивает счетчик на 1 и возвращает TRUE. Последующий вызов функции WaitForSingleObject уменьшает значение счетчика на 1, т. е. возвращает его к исходному состоянию.
Если текущее значение равно максимальному, то эта функция вернет FALSE и в N останется исходное значение.
Пример.
HANDLE S = CreateSemaphore(NULL, MaxCount, MaxCount, "Sem1");
// Клиент ресурса, если он находится в другом потоке, соединяется с семафором:
HANDLE S = OpenSemaphore(SEMAPHORE_ALL_ACCESS, true, "Sem1");
DWORD Result = WaitForSingleObject(S, 2000);
if (Result == WAIT_OBJECT_0) {
// действия с ресурсами
// освобождение ресурса:
ReleaseSemaphore(S, 1, NULL);
}
else {
// отсутствие доступа к ресурсу
}
Пример (C).
В следующем примере объект-семафор используется для ограничения числа потоков, которые могут выполнять определенную задачу. Сначала он использует функцию CreateSemaphore для создания семафора и указания начального и максимального количества, затем он использует функцию CreateThread для создания потоков.
Прежде чем поток пытается выполнить задачу, он использует функцию WaitForSingleObject, чтобы определить, позволяет ли текущий счетчик семафора сделать это. Параметр времени ожидания функции ожидания установлен в ноль, поэтому функция немедленно возвращается, если семафор находится в состоянии без сигнала. WaitForSingleObject уменьшает число семафоров на единицу.
Когда поток завершает задачу, он использует функцию ReleaseSemaphore для увеличения счетчика семафора, что позволяет другому ожидающему потоку выполнить задачу.
#include <stdio.h>
#include <locale.h>
#include <windows.h>
#define MAX_SEM_COUNT 10 /// Счетчик семафора
#define THREADCOUNT 51 /// Количество потоков
HANDLE ghSemaphore;
DWORD WINAPI ThreadProc(LPVOID);
int main() {
setlocale(LC_ALL, "Russian");
HANDLE aThread[THREADCOUNT]; /// Дескрипторы
DWORD ThreadID;
int i;
/// Создаем семафор
ghSemaphore = CreateSemaphore(NULL, MAX_SEM_COUNT, MAX_SEM_COUNT, NULL);
if (ghSemaphore == NULL) { /// Если ошибка
printf("CreateThread ошибка: %d\n", GetLastError());
return 1;
}
/// Создаем работающие потоки
for (i = 0; i < THREADCOUNT; i++) {
aThread[i] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) ThreadProc, NULL, 0, &ThreadID);
if (aThread[i] == NULL) { /// Если ошибка
printf("CreateThread ошибка: %d\n", GetLastError());
return 1;
}
}
/// Ждем все потоки INFINITE-time
WaitForMultipleObjects(THREADCOUNT, aThread, TRUE, INFINITE);
/// Закрываем дескрипторы потоков
for (i = 0; i < THREADCOUNT; i++)
CloseHandle(aThread[i]);
CloseHandle(ghSemaphore); /// и семафора
return 0;
}
/// Функция потоков
DWORD WINAPI ThreadProc(LPVOID lpParam) {
DWORD dwWaitResult;
BOOL bContinue = TRUE;
while (bContinue) {
/// Попробуем войти в семафор
dwWaitResult = WaitForSingleObject(ghSemaphore, 0L);
/// При 0L будут возникать случаи "время истекло"
/// При INFINITE везде будет "успешное выполнение"
switch (dwWaitResult) {
/// Объект семафора в сигнальном состоянии
case WAIT_OBJECT_0:
/// Выполняем задачу
printf("Поток %d: успешное выполнение\n", GetCurrentThreadId());
/// Остановка цикла while
bContinue = FALSE;
/// Имитация работы потока (он спит)
Sleep(5);
/// Выпуск семафора после завершения задачи