На главную Содержание
Семафор является одним из объектов синхронизации и содержит счетчик,
учитывающий количество потоков, обратившихся к данному ресурсу. Чтобы
получить доступ к ресурсу, защищенному семафором, поток должен вызвать
одну из wait-функций
и передать ей дескриптор данного семафора. Если в этот момент счетчик
семафора имеет значение больше нуля, то поток получает доступ к ресурсу,
а счетчик семафора уменьшается на единицу. Если же при вызове wait-функции счетчик семафора уже
равен нулю, то выполнение потока приостанавливается до тех пор, пока не
увеличится счетчик семафора (или пока не истечет время ожидания,
задаваемое при вызове wait-функции).
Поток создает семафор с помощью функции CreateSemaphore():
HANDLE CreateSemaphore( LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, LONG lInitialCount, LONG lMaximumCount, LPCTSTR lpName );
Первый параметр функции является указателем на структуру
SECURITY_ATTRIBUTES. Эта структура содержит информацию о защите
создаваемого объекта. Обычно в качестве этого параметра передается NULL
и в этом случае создается объект с защитой по умолчанию.
Параметр lInitiаlСоunt
содержит начальное значение счетчика семафора.
Параметр lMaximumCount
указывает максимально возможное значение счетчика.
Параметр lpName является
указателем на строку, в которой содержится имя данного семафора.
После того как семафор успешно создан, поток может обратиться к
ресурсу, защищенному семафором, с помощью одной из wait-функций. При этом wait-функции передается дескриптор
семафора.
После завершения работы с ресурсом поток увеличивает значение счетчика
с помощью функции ReleaseSemaphore():
BOOL ReleaseSemaphore( HANDLE hSemaphore, LONG lReleaseCount, LPLONG lpPreviousCount );
Первый параметр функции - дескриптор семафора.
Второй параметр указывает величину, на которую должно быть увеличено
значение счетчика.
Параметр lpPreviousCount
является указателем на переменную, в которой функция сохраняет
предыдущее значение счетчика. Если сохранять это значение не нужно, то в
качестве последнего параметра передается NULL.
Рассмотрим пример использования семафора. Создадим простое оконное
приложение и поместим кнопку на главное окно. При нажатии на кнопку
приложение будет создавать 3 потока, задача каждого из которых -
нарисовать квадрат на главном окне приложения. Цвет квадрата выбирается
случайным образом.
Мы передаем функции CreateSemaphore()
единицы в качестве второго и третьего параметров. Это означает, что в
любой момент времени только один поток сможет использовать защищенный
ресурс. Причем обратиться к ресурсу можно сразу после создания семафора,
поскольку его счетчик > 0.
Функция окна программы будет выглядеть так:
LRESULT CALLBACK _export MainWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { DWORD dwIdThread; switch (msg) { case WM_COMMAND: { if(wParam == BTN_PAINT) { // создаем потоки, в качестве параметра передаем дескриптор // главного окна for(int i = 0; i < countThreads; ++i) { hThreads[i] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)threadRoutine, (LPVOID)&hwndMain, 0, (LPDWORD)&dwIdThread); if(hThreads[i] == NULL) { MessageBox(NULL, "Ошибка при создании потока", szWindowTitle, MB_ICONSTOP); } else CloseHandle(hThreads[i]); } } return 0; } case WM_DESTROY: { PostQuitMessage(0); return 0; } } return DefWindowProc(hwnd, msg, wParam, lParam); }
При нажатии кнопки "Paint" создаются три потока. Каждый поток получает
в качестве параметра дескриптор главного окна.
Функция threadRoutine(), которая запускается при создании потока:
// увеличиваем на единицу счетчик семафора, чтобы другие потоки // могли выполнить свою задачу if(hSemaphore != NULL) ReleaseSemaphore(hSemaphore, 1, NULL); return 0; }
После вызова функции WaitForSingleObject()
(если она не возвращает WAIT_FAILED) поток ждет момента, когда значение
счетчика семафора будет больше нуля.
Когда поток завершает работу с контекстом отображения, он увеличивает
значение счетчика семафора на единицу.