Уведомление при помощи объекта Событие
Приведем примерные действия по использованию объекта событие.
Определим структуру, которая будет использоваться для хранения общих переменных главного и вторичного потоков:
struct PARAM { BOOL type; // тип выполняемых потоком действий HWND wnd; // окно, в оконной процедуре которого создается поток BOOL stop; // признак завершения работы потокаHANDLE event;// объект-событие, поток работает, если оно установлено};Рассмотрим оконную процедуру, в которой создается поток, выполняющийся параллельно с главным потоком. Поток запускается на этапе создания окна главного потока, но находится в приостановленном состоянии до тех пор, пока пользователь его не выберет команду меню, приводящую поток в действие. Приостановкой потока и его возобновлением, а также типом выполняемого потоком действия, управляет пользователь при помощи команд меню.
#define IDM_CIRCLES 1#define IDM_BARS 2#define IDM_STOP 3#define IDM_BEGIN 4. . . LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam){ static struct PARAM p; // используется как общая память потоков . . . switch(msg) { case WM_CREATE: { // создание изначально сброшенного объекта событие – третий // аргумент равен FALSE, при этом объект событие не будет // автоматически становиться занятым при выходе из функции // WaitForSingleObject – второй аргумент равен TRUE // (первый и посл. аргументы - для объектов, разделяемых процессами)p.event=CreateEvent(NULL,TRUE,FALSE,NULL); // запись в общую память потоков, создание и запуск потока, // который выполняется, если объект событие установленоp.type=0; p.wnd=hWnd; p.stop=0; _beginthread(ThreadFun,0,(void *)(&p)); }; break; case WM_COMMAND: // обработка сообщений от меню { switch(LOWORD(wParam)) { case IDM_BEGIN: // выполнение потока { // сделать события установленнымSetEvent(p.event); }; break; case IDM_STOP: // приостановка потока { // сделать события сброшенымResetEvent(p.event); }; break; case IDM_CIRCLES: // изменить тип действия { // записать тип выполняемого потоком действияp.type=0; }; break; case IDM_BARS: // изменить тип действия { // записать тип выполняемого потоком действияp.type=1; }; break; } }; return 0; case WM_DESTROY: { // записать признак завершения потокаp.stop=1; PostQuitMessage(0); }; return 0l; // обработка других сообщений . . . default: return DefWindowProc(hWnd, msg, wParam, lParam); } return 0l;}Функция создаваемого вторичного потока в данном случае может иметь следующее определение:
void ThreadFun(void *lpvoid) { // автоматические переменные потока . . . // получение адреса общей памяти потоков struct PARAM *p=(struct PARAM *)lpvoid; while(!p->stop) // пока признак завершения не установлен { // ожидание установки объекта события - если объект событие установлено, // поток будет работать, если сброшено, то поток будет ждать, // пока эта функция возвратит управление WaitForSingleObject(p->event,INFINITE); // выполнение действия, определяемогоp->type, при этом может // использоваться дескриптор создавшего поток окна, он хранится в p->wnd . . . } _endthread(); // полная остановка потока }Вторичный поток создается при инициализации окна главного потока и готов к выполнению на протяжении существования этого окна, т.е. пока приложение не завершит свою работу. Но выполняться он будет только в случае, если пользователь при помощи команды меню инициирует установку объекта событие. При установке объекта события в сброшенное состояние поток вновь приостанавливается
Семафоры
Объекты ядра «семафор» используются для учета ресурсов Как и все объекты ядра, они содержат счетчик числа пользователей, но, кроме того, поддерживают два 32 битных значения со знаком: одно определяет максимальное число ресурсов (контролируемое семафором), другое используется как счетчик текущего числа ресурсов
Примером использования семафора может служит реализация веб-сервера с ограниченным числом обрабатываемых запросов. Семафор мог бы инициализироваться максимально допустимым количество запросов и потоков их обрабатывающих. Этот счетчик увеличивается на 1 в момент приема очередного клиентского запроса и на столько же уменьшается, когда запрос передается на обработку одному из серверных потоков в пуле.
Для семафоров определены следующие правила:
- когда счетчик текущего числа ресурсов становится больше 0, семафор переходит в свободное состояние,
- если этот счетчик равен 0, семафор занят,
- система не допускает присвоения отрицательных значений счетчику текущего числа ресурсов;
- счетчик текущего числа ресурсов не может быть больше максимального числа ресурсов
Не путайте счетчик текущего числа ресурсов со счетчиком числа пользователей объекта-семафора
Объект ядра «семафор» создается вызовом CreateSemapbore
HANDLE CreateSemaphore( PSECURITY_ATTRIBUTE psa, LONG lInitialCount, LONG lMaximumCount, PCTRTR pszName)
Любой процесс может получить свой («процессо-зависимый») описатель существующего объекта «семафор», вызвав OpenSemaphore
HANDLE OpenSemaphore( DWORD fdwAccess, BOOL bInhentHandle, PCTSTR pszName);
Параметр lMaximumCount сообщает системе максимальное число ресурсов, обрабатываемое приложением Поскольку это 32-битное значение со знаком, пре дельное число ресурсов можетдостигать 2 147 483 647 Параметр lInitiа1Соипt указывает, сколько из этих ресурсов доступно изначально (на данный момент) При инициализяции серверного процесса клиентских запросов нет, поэтому я вызывается CreateSemaphore так
HANDLE hSem = CreateSemaphore(NULL, 0, 5, NULL);
Это приводит к созданию семафора со счетчиком максимального числа ресурсов равным 5, при этом изначально ни один ресурс не доступен (Кстати, счетчик числа пользователей данного объекта ядра равен 1, не запутайтесь в счетчиках) Поскольку счетчику текущего числа ресурсов присвоен 0 семафор находится в занятом состоянии А это значит, что любой поток, ждущий семафор, просто засыпает
Поток получает доступ к ресурсу, вызывая одну из Wait-функций и передавая ей описатель семафора, который охраняет этот ресурс Wait-функция проверяет у сема фора счетчик текущего числа ресурсов если его значение больше 0 (семафор свободен), уменьшает значение этого счетчика на 1, и вызывающий поток остается планируемым
Если Wait-функция определяет, что счетчик текущего числа ресурсов равен 0 (семафор занят), система переводит вызывающий поток в состояние ожидания Когда другой поток увеличит значение этого счетчика, система вспомнит о ждущем потоке и снова начнет выделять ему процессорное время (а он, захватив ресурс, уменьшит значение счетчика на 1).
Поток увеличивает значение счетчика текущего числа ресурсов, вызывая функцию ReleaseSemaphore
BOOL ReleaseSemaphore( HANDLEhSem, LONG lReleaseCount, PLONG pPreviousCount);
Она просто складывает величину lReleaseCount со значением счетчика текущего числа ресурсов. Обычно в параметре lReleaseCount передают 1, но это вовсе не обязательно. Функция возвращает исходное значение счетчика ресурсов в *plPreviousCount.
Мьютексы
Объекты ядра «мьютексы» гарантируют потокам взаимоисключающий доступ к единственному ресурсу. Отсюда и произошло название этих объектов (mutual exclusion, mutex). Они содержат счетчик числа пользователей, счетчик рекурсии и переменную, в которой запоминается идентификатор потока. Мьютексы ведут себя точно так же, как и критические секции. Однако, если последние являются объектами пользовательского режима, то мьютексы — объектами ядра. Кроме того, единственный объект-мьютекс позволяет синхронизировать доступ к ресурсу нескольких потоков из разных процессов; при этом можно задать максимальное время ожидания доступа к ресурсу.
Как правило, с их помощью защищают блок памяти, к которому обращается множество потоков Если бы потоки одновременно использовали какой-то блок памяти, данные в нем были бы повреждены. Мьютексы гарантируют, что любой поток получает монопольный доступ к блоку памяти, и тем самым обеспечивают целостность данных.
Для мьютексов определены следующие правила:
- если его идентификатор потока равен 0 (у самого потока не может быть та кой идентификатор), мьютекс не захвачен ни одним из потоков и находится в свободном состоянии;
- если его идентификатор потока не равен 0, мьютекс захвачен одним из потоков и находится в занятом состоянии;
Для использования объекта-мьютекса один из процессов должен сначала создать его вызовом CreateMutex:
HANDLE CreateMutex( PSECURITY_ATTRIBUTES psa, BOOL fIniLialOwner, PCTSTR pszName);
Любой процесс может получить свой («процессо-зависимый») описатель существующего объекта «мьютекс», вызвав OpenMutex:
HANDLE OpenMutex( DWORD fdwAccess, B00L bInheritHandle, PCTSTR pszName);
Параметр fInitialOwner определяет начальное состояние мъютекса. Если в нем передается FALSE (что обычно и бывает), объект-мьютекс не принадлежит ни одному из потоков и поэтому находится в свободном состоянии. При этом его идентификатор потока и счетчик рекурсии равны 0 Если же в нем передается TRUE, идентификатор потока, принадлежащий мьютексу, приравнивается идентификатору вызывающего потока, а счетчик рекурсии получает значение 1. Поскольку теперь идентификатор потока отличен от 0, мьютекс изначально находится в занятом состоянии.
Поток получает доступ к разделяемому ресурсу, вызывая одну из Wait-функций и передавая ей описатель мьютекса, который охраняет этот ресурс. Wait-функция проверяет у мьютекса идентификатор потока, если его значение не равно 0, мьютекс свободен, в ином случае оно принимает значение идентификатора вызывающего потока, и этот поток остается планируемым.
Если Wait-функция определяет, что у мьютекса идентификатор потока не равен 0 (мьютекс занят), вызывающий поток переходит в состояние ожидания. Система за поминает это и, когда идентификатор обнуляется, записывает в него идентификатор ждущего потока, асчетчику рекурсии присваивает значение 1, после чего ждущий поток вновь становится планируемым. Все проверки и изменения состояния объекта-мьютекса выполняются на уровне атомарного доступа.
Для мьютексов сделано одно исключение в правилах перехода объектов ядра из одного состояния в другое Допустим, поток ждет освобождения занятого объекта мьютекса В этом случае поток обычно засыпает (переходит в состояние ожидания). Однако система проверяет, не совпадает ли идентификатор потока, пытающегося захватить мьютекс, с аналогичным идентификатором у мьютекса Если они совпадают, система по-прежнему выделяет потоку процессорное время, хотя мьютекс все еще занят. Подобных особенностей в поведении нет ни у каких других объектов ядра в системе.
Когда поток, занимающий ресурс, заканчивает с ним работать, он должен освободить мьютекс вызовом функции ReleaseMutex
BOOL ReleaseMutex(HANDLE hMutex);
Эта функция уменьшает счетчик рекурсии в объекте-мьютексе на 1. Если данный объект передавался во владение потоку неоднократно, поток обязан вызвать Release Mutex столько раз, сколько необходимо для обнуления счетчика рекурсии.
Дата добавления: 2017-01-26; просмотров: 1218;