Создание семафора


На главную
Содержание

Семафор является одним из объектов синхронизации и содержит счетчик, учитывающий количество потоков, обратившихся к данному ресурсу. Чтобы получить доступ к ресурсу, защищенному семафором, поток должен вызвать одну из 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 потока, задача каждого из которых - нарисовать квадрат на главном окне приложения. Цвет квадрата выбирается случайным образом.

Объявление глобальных переменных:
// количество потоков
const unsigned countThreads = 3;
// массив, содержащий дескрипторы потоков
HANDLE hThreads[countThreads];
// дескриптор семафора
HANDLE hSemaphore;
// имя семафора
const char lpSemaphoreName[] = "MySemaphore";
В функции WinMain() мы создаем семафор:
int PASCAL
WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpszCmdLine, int nCmdShow)
{
MSG msg;
hInst = hInstance;
if(!InitApp(hInstance))
return FALSE;
  // создаем семафор
hSemaphore = CreateSemaphore(NULL, 1, 1, lpSemaphoreName);
if(hSemaphore == NULL) {
MessageBox(NULL, "Ошибка при создании семафора", szWindowTitle,
MB_ICONSTOP);
return FALSE;
}
  hwndMain = CreateWindow(szMainClassName, szWindowTitle,
WS_DLGFRAME | WS_SYSMENU,
140, 80, 550, 400, 0, 0, hInstance, NULL);
if(!hwndMain)
return FALSE;
ShowWindow(hwndMain, nCmdShow);
UpdateWindow(hwndMain);
CreateWindow("button", "Paint", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
20, 320, 90, 30, hwndMain, (HMENU)BTN_PAINT, hInstance, NULL);
while(GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
  // закрываем семафор
CloseHandle(hSemaphore);

return msg.wParam;
}
Мы передаем функции 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(), которая запускается при создании потока:
DWORD threadRoutine(HWND* phWnd)
{
// выполняем ожидание семафора
if(WaitForSingleObject(hSemaphore, 30000) == WAIT_FAILED)
return 0;
  // рисуем прямоугольник
HDC hDC = GetDC(*phWnd);
HBRUSH hBrush, hOldBrush;
randomize();
int color = rand() % 7;
switch(color) {
case 0: hBrush = CreateSolidBrush(RGB(0, 0, 255)); break;
case 1: hBrush = CreateSolidBrush(RGB(0, 255, 0)); break;
case 2: hBrush = CreateSolidBrush(RGB(255, 0, 0)); break;
case 3: hBrush = CreateSolidBrush(RGB(0, 255, 255)); break;
case 4: hBrush = CreateSolidBrush(RGB(255, 255, 0)); break;
case 5: hBrush = CreateSolidBrush(RGB(255, 0, 255)); break;
case 6: hBrush = CreateSolidBrush(RGB(255, 255, 255)); break;
}
hOldBrush = (HBRUSH)SelectObject(hDC, hBrush);
Rectangle(hDC, 20, 20, 320, 220);
SelectObject(hDC, hOldBrush);
DeleteObject(hBrush);
ReleaseDC(*phWnd, hDC);
UpdateWindow(*phWnd);
Sleep(1000);
  // увеличиваем на единицу счетчик семафора, чтобы другие потоки
// могли выполнить свою задачу
if(hSemaphore != NULL)
ReleaseSemaphore(hSemaphore, 1, NULL);
return 0;
}
После вызова функции WaitForSingleObject() (если она не возвращает WAIT_FAILED) поток ждет момента, когда значение счетчика семафора будет больше нуля.
Когда поток завершает работу с контекстом отображения, он увеличивает значение счетчика семафора на единицу.

Скачать этот пример полностью можно здесь.

Содержание
На главную

Rambler's Top100
Хостинг от uCoz