Синхронизация с использованием семафоров

Семафоры позволяют обеспечить доступ к ресурсу для заранее опреде­ленного ограниченного приложением количества задач. Все остальные зада­чи, пытающиеся получить доступ сверх установленного лимита, будут пере­ведены при этом в состояние ожидания до тех пор, пока какая-либо задача, получившая доступ к ресурсу раньше, не освободит ресурс, связанный с дан­ным семафором.

Семафор может находиться в отмеченном или неотмеченном состоянии. Приложение выполняет ожидание семафора при помощи функций WaitForS-ingleObject или WaitForMultipleObject. С каждым семафором связывается счетчик, начальное и максимальное значение которого задается при создании семафора. Значение этого счетчика уменьшается, когда задача вызывает для семафора функции WaitForSingleObject или WaitForMultipleObject, и увеличи­вается при вызове специально предназначенной для этого функции Release-Semaphore.

Для создания семафора приложение должно вызвать функцию Create-Semaphore:

HANDLE CreateSernaphore(

LPSECURITYATreBUTES IpSemaphoreAttributes, // атрибуты защиты

LONG llnitialCount, // начальное значение счетчика семафора

LONG lMaximiupCount, // максимальное значение счетчика

LPCTSTR IpName); // адрес строки с именем семафора

В качестве атрибутов защиты вы можете передать NULL.

Через параметры UnitialCount и lMaximumCount передается соответст­венно начальное и максимальное значение счетчика семафора.

Имя семафора указывается аналогично именам рассмотренных ранее объектов.

Если семафор используется для синхронизации задач, созданных в рам­ках одного процесса, вы можете создать безымянный семафор, указав в каче­стве параметра IpName функции CreateSemaphore значение NULL. В том слу­чае, когда нужно синхронизировать задачи разных процессов, следует опре­делить имя семафора. При этом один процесс создает семафор с помощью функции CreateSemaphore, а второй открывает его.

Функция возвращает идентификатор семафора или NULL в случае ошибки.

Существующий семафор можно открыть функцией OpenSemaphore:

HANDLE OpenSemaphore(

DWORD fdwAccess, // требуемый доступ

BOOL . flnherit, // флаг наследования

LPCTSTR IpszSemaphoreName); // адрес имени семафора

Назначение всех параметров функции аналогично рассмотренному ра­нее для функций OpenEvent, OpenMutex.

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

BOOL ReleaseSemaphore(

HANDLE hSemaphore, // идентификатор семафора

LONG cReleaseCount, // значение инкремента

LPLONG IplPreviousCount); // адрес переменной для записи

предыдущего значения счетчика семафора.

Для уничтожения семафора вы должны передать его идентификатор функции CloseHandle. При завершении процесса все созданные им семафоры уничтожаются автоматически.

При успешном завершении функция возвращает значение TRUE.

Варианты заданий

l. Продемонстрировать возможность управления задачами в мультизадачной ОС WINDOWS, используя пример мультизадачного приложения MiltiSDI, приведенного ниже, и функции управления задачами SetThreadPriority, GetThreadPriority, Sllep, SuspendThread, ResumeThread.

2. Модифицировать приложение MultiSDI таким образом, чтобы в качестве средства синхронизации задач вместо "критических секций" использова­лись бы средства синхронизации типа "объект-событие".

3. Модифицировать приложение MultiSDI таким образом, чтобы в качестве средства синхронизации задач вместо "критических секций" использова­лись бы средства синхронизации типа "объект Mutex".

4. Модифицировать приложение MultiSDI таким образом, чтобы в качестве средства синхронизации задач вместо "критических секций" использова­лись бы средства синхронизации типа "семафор".

5. Модифицировать приложение MultiSDI таким образом, чтобы подсчиты-валось общее число обращений к задачам, рисующим изображение в глав­ном окне приложения. Для подсчета числа обращений использовать гло­бальную переменную и "блокирующие функции".

Пример мультизадачного приложения

Главная задача приложения рисует в главном окне приложения во время обработки сообщения WM_PAINT. Кроме того, она создает еще три зада­чи, которые рисуют в окне приложения соответственно эллипсы, прямоуголь­ники и текстовую строку TEXT. Цвет и размеры фигур, а также цвет текстовой строки и цвет фона, на котором отображается эта строка, выбирается случайным образом.

Так как все задачи рисуют в одном окне для обеспечения последовательного доступа к этому ресурсу - окну для рисования- используется про­стейшее средство синхронизации - критическая секция.

Главный файл исходных текстов приложения MultiSDI приведен в лис­тинге 1.

Лис­тинг 1.

#include <windows.h>

#include <windowsx.h>

#include <stdio.h>

#include "resource.h"

#include "afxres.h"

#include "multisdi.h"

HINSTANCE hInst;

char szAppName[] = "MulniSDI";

char szAppTitle[] = "MultiThead SDI Application";

CRITICAL_SECTION csWindowPaint; // критическая секция для рисования

в окне

BOOL fTerminate = FALSE; // признак завершения всех задач

HANDLE hThreads[3]; // массив идентификаторов запущенных задач

DWORD dwIDThread; // переменная для хранения системного идентификатора задачи

//Функция WinMain

int APIENTRY

WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,

LPSTR lpCmdLine, int nCmdShow)

{ WNDCLASSEX wc;

HWND hWnd;

MSG msg;

hInst = hInstance; // сохраняем идентификатор приложения

// Проверяем, не было ли это приложение запущено ранее

hWnd = FindWindow(szAppName, NULL);

if(hWnd)

{ // Если было, выдвигаем окно приложения на передний план

if(IsIconic(hWnd))

ShowWindow(hWnd, SW_RESTORE);

SetForegroundWindow(hWnd);

return FALSE; }

// Регистрируем класс окна

memset(&wc, 0, sizeof(wc));

wc.cbSize = sizeof(WNDCLASSEX);

wc.hIconSm = LoadImage(hInst,

MAKEINTRESOURCE(IDI_APPICONSM),

IMAGE_ICON, 16,16,0);

wc.style = 0;

wc.lpfnWndProc = (WNDPROC)WndProc;

wc.cbClsExtra = 0;

wc.cbWndExtra = 0;

wc.hInstance = hInst;

wc.hIcon = LoadImage(hInst,

MAKEINTRESOURCE(IDI_APPICON),

IMAGE_ICON, 32, 32, 0);

wc.hCursor = LoadCursor(NULL, IDC_ARROW);

wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);

wc.lpszMenuName = MAKEINTRESOURCE(IDR_APPMENU);

wc.lpszClassName =szAppName;

if(!RegisterClassEx(&wc))

if(!RegisterClass((LPWNDCLASS)&wc.style))

return FALSE;

// Создаем главное окно приложения

hWnd = CreateWindow(s2AppName, s2AppTitle, WS_OVERLAPPEDWINDOW,

CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,

NULL, NULL, hInst, NULL);

if(!hWnd) return(FALSE);

// Отображаем окно и запускаем цикл обработки сообщений ShowWindow(hWnd, nCmdShow);

UpdateWindow(hWnd);

while(GetMessage(&msg, NULL, 0, 0))

{ TranslateMessage(&msg);

DispatchMessage(&msg); }

return msg.wParam;

}

// Функция WndProc

LRESULT WINAPI

WndProc(HWND hWnd, UINT msg, WPARAM wParam,

LPARAM lParam)

{ switch(msg)

{

HANDLE_MSG(hWnd, WM _CREATE, WndProc _OnCreate); HANDLE_MSG(hWnd, WM_DESTROY, WndProc_OnDestroy); HANDLE_MSC(hWnd, WM_PAINT, WndProc _OnPaint);

HANDLE_MSG(hWnd, WM_COMMAND, WndProc_ OnCommand);

default:

return(DefWindowProc(hWnd, msg, wParam, lParam));

}

}

//Функция WndProc_OnCreate

BOOL WndProc_OnCreate(HWND hWnd, LPCREATESTRUCT lpCreat-

eStruct)

{ // Инициализируем критическую секцию

InitializeCriticalSection(&csWindowPaint);

fTerminate = FALSE; // сбрасывает флаг завершения задач

//Запускаем три задачи, сохраняя их идентификаторы в массиве

hThreads[0] = CreateThread(NULL, 0,

(LPTHREAD_START_ROUTINE)PaintEllipse, (LPVOID)hWnd, 0, (LPDWORD)&dwIDThread);

hThreads[l] = CreateThread(NULL, 0,

(LPTHREAD_START_ROUTINE)PaintRect, (LPVOID)hWnd, 0, (LPDWORD)&dwIDThread);

hThreads[2] = CreateThread(NULL, 0,

(LPTHREAD_START_ROUTINE)PaintText, (LPVOID)hWnd, 0, (LPDWORD)&dwIDThread);

return TRUE; }

// Функция WndProcOnDestroy

#pragma warning(disable: 4098)

void WndProc_OnDestroy(HWND hWnd)

{ fTerminate = TRUE; // устанавливает флаг завершения задач

// Дожидаемся завершения всех трех задач

WaitForMultipleObjects(3, hThreads, TRUE, INFINITE);

// Перед завершением работы удаляем критическую секцию

DeleteCriticalSection(&csWindowPaint);

// Останавливаем цикл обработки сообщений

PostQuitMessage(O);

return 0L; }

// Функция WndProc_OnPaint

#pragma warning(disable: 4098)

void WndProc_OnPaint(HWND hWnd)

{ HDC hdc;

PAINTSTRUCT ps;

RECT rc;

// Входим в критическую секцию

EnterCriticalSection(&csWmdowPaint);

// Перерисовываем внутреннюю область окна

hdc = BeginPaint(hWnd, &ps);

DrawText(hdc, "SDI Window", -1, &rc,

DT_SINGLELINE | DT_CENTER | DT_VCENTER);

EndPaint(hWnd, &ps);

// Выходим из критической секции

LeaveCriticalSection(&csWindowPaint);

return 0; }

// Функция WndProc_OnCommand

#pragma warning(disable: 4098)

void WndProc_OnCommand(HWND hWnd, int id,

HWND hwndCtl, UINT codeNotify)

{ switch(id)

{ case ID_FILE_EXIT:

{ // Завершаем работу приложения

PostQuitMessage(0);

return 0L;

breaк; }

case ID_HELP_ABOUT:

{ MessageBox(hWnd,

"Multithread SDI Application\n",

szAppTitle, MB _OK | MB_ICONINFORMATION);

return OL;

break; }

default:

break;

}

return FORWARD_WM_COMMAND(hWnd, id, hwndCtl, codeNotify, DefWindowProc); }

// Функция задачи PaintEllipse

void PaintEllipse(void *hwnd)

{ HDC hDC;

RECT rect;

LONG xLeft, xRight, yTop, yBotton;

short nRed, nGreen, nBlue;

HBRUSH hBrush, hOldBrush;

// Инициализация генератора случайных чисел

srand((unsigned int)hwnd);

// Задача будет работать в бесконечном цикле

while(! fTerminate)

{// Входим в критическую секцию

EnterCriticalSection(&csWindowPaint);

// Отображаем эллипс, который имеет случайный цвет, форму и расположение

hDC = GetDC(hwnd); // получаем контекст отображения

// Получаем случайные цветовые компоненты

nRed = rand()% 255;

nGreen = rand() % 255;

nBlue = rand()% 255;

// Получаем случайные размеры эллипса

GetWindowRect(hwnd, &rect);

xLeft = rand() % (rect.left + 1);

xRight = rand() % (rect.right + 1);

уТор = rand() %,(rect.top + 1);

yBotton = rand() % (rect.bottom + 1);

// Создаем кисть на основе случайных цветов

hBrush = CreateSolidBrush(RGB(nRed, nGreen, nBlue));

// Выбираем кисть в контексте отображения

hOldBrush = SelectObject(hDC, hBrush);

// Рисуем эллипс

Ellipse(hDC, min(xLeft, xRight), min(yTop, yBotton),

max(xLeft, xRight), max(yTop, yBotton));

// Выбираем старую кисть в контексте отображения

SelectObject(hDC, hOldBrush);

// Удаляем созданную кисть

DeleteObject(hBrush);

// Освобождаем контекст отображения

ReleaseDC(hwnd, hDC);

// Выходим из критической секции

LeaveCriticalSection(&csWindowPaint);

// Выполняем задержку на 500 миллисекунд

Sleep(500); }

}

// Функция задачи Paint Rect

void PaintRect(void *hwnd)

{ HDC hDC;

RECT rect;

LONG xLeft, xRight, yTop, yBotton;

short nRed, nGreen, nBlue;

HBRUSH hBrush, hOldBrush;

srand((unsigned int)hwnd + 1);

while(!fTerminate)

{ EnterCriticalSection(&csWindowPaint);

hDC = GetDC(hwnd);

nRed = rand() % 255;

nGreen = rand() % 255;

nBlue = rand() % 255;

GetWindowRect(hwnd, &rect);

xLeft =rand() % (rect.left + 1);

xRight = rand() % (rect.right + 1);

yTop = rand() % (rect.top + 1);

yBotton = rand() % (rect.bottom + 1);

hBrush =CreateSolidBrush(RGB(nRed, nGreen, nBlue));

hOldBrush = SelectObject(hDC, hBrush);

Rectangle(hDC, min(xLeft, xRight), min(yTop, yBotton),

max(xLeft, xRight), max(yTop, yBotton));

SelectObject(hDC, hOldBrush);

DeleteObject(hBrush);

ReleaseDC(hwnd, hDC);

LeaveCriticalSection(&csWindowPaint);

Sleep(500); }

}

// Функция задачи PaintText

void PaintText(void *hwnd)

{ HDC hDC;

RECT rect;

LONG xLeft, xRight, yTop, yBotton;

short nRed, nGreen, nBlue;

srand((unsigned int)hwnd + 2);

while(!fTerminate)

{ EnterCriticalSection(&csWindowPaint);

hDC = GetDC(hwnd); GetWindowRect(hwnd, &rect);

xLeft = rand() % (rect.left + 1);

xRight« rand() % (rect.right + 1);

yTop = rand() % (rect.top +1);

yBotton = rand() % (rect.bottom + 1);

// Устанавливаем случайный цвет текста

nRed = rand() % 255;

nGreen = rand()% 255;

nBlue = rand() % 255;

SetTextColor(hDC, RGB(nRed, nGreen, nBlue));

// Устанавливаем случайный цвет фона

nRed = rand()% 255;

nGreen = rand() % 255;

nBlue = rand()% 255;

SetBkColor(hDC, RGB(nRed, nGreen, nBlue));

TextOut(hDC, xRight - xLeft,

yBotton - yTop, "TEXT", 4);

ReIeaseDC(hwnd, hDC);

LeaveCriticalSection(&csWindowPaint);

Sleep(500); }

}

Файл Multisdi.h (листинг 2) содержит прототипы функции, определенных в приложении MuitiSD.Это функция главного окна приложения WndProc.Функция обработки сообщений WM_COMMAND, WM_DESTROY,WM_PAINT,WM_CREATE с именами WndProc_OnCommand, WndProc_OnDestroy, WndProc_OnPAINT, WndProc_OnCREATE, а также функции задач PaintEclipse,PaintRect и PaintText.

Листинг 2.

LRESULT WINAPI.

WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParamV

BOOL WndProc_OnCreate(HWND hWnd, LPCREATESTRUCT lpCreateStruct);

void WndProcJ3nDestroy(HWND hWnd);

void WndProc_OnPaint(HWND hWnd);

void WndProc_OnCommand(HWND hWnd, int id,

HWND hwndCtl, UINT codeNotify);

void PaintEllipse(void *hwnd);

void PaintRect(void *hwnd);

void PaintText(void *hwnd);

Файлы resorse.h и multisdi.rc создаются автоматически с использовани­ем редактора ресурсов системы разработки приложений Visual C++.

Литература

1. Фролов А.В., Фролов Г.В. Программирование для WINDOWS NT. Биб­лиотека системного программиста; Т. 26.- М.: ДИАЛОГ-МИФИ, 1996.

2. Фролов А.В., Фролов Г.В. Программирование для WINDOWS NT. Биб­лиотека системного программиста; Т. 27.- М.: ДИАЛОГ-МИФИ, 1996.

3. Кастер Х. Основы WINDOWS NT и NTFS. - M:Microsoft Press, 1996.

4. Дженнингс P. Windows 95 в подлиннике. Спб.: BHV - Санкт-Петер­бург, 1995.

Наши рекомендации