КАТЕГОРИИ:
АстрономияБиологияГеографияДругие языкиДругоеИнформатикаИсторияКультураЛитератураЛогикаМатематикаМедицинаМеханикаОбразованиеОхрана трудаПедагогикаПолитикаПравоПсихологияРиторикаСоциологияСпортСтроительствоТехнологияФизикаФилософияФинансыХимияЧерчениеЭкологияЭкономикаЭлектроника
|
Функция SleepПоток может сообщить системе не выделять ему процессорное время на определенный период, вызвав: VOID Sleep(DWORD dwMilliseconds); Эта функция приостанавливает поток на dwMilliseconds миллисекунд. Отметим несколько важных моментов, связанных с функцией Sleep. - Вызывая Sleep, поток добровольно отказывается от остатка выделенного ему кванта времени - Система прекращает выделять потоку процессорное время на период, пример но равный заданному. - Вы можете вызвать Sleеp и передать в dwMilliseconds значение INFINITE, вообще запретив планировать поток. Но это не очень практично — куда лучше корректно завершить поток, освободив его стек и объект ядра. - Вы можете вызвать Sleep и передать в dwMilliseconds нулевое значение. Тогда Вы откажетесь от остатка своего кванта времени и заставите систему подключить к процессору другой поток. Однако система может снова запустить Ваш поток, если других планируемых потоков с тем же приоритетом нет. Поток можно завершитьчетырьмя способами: 1.функция потока возвращает управление (рекомендуемый способ). Функцию потока следует проектировать так, чтобы поток завершался только после того, как она возвращает управление. Это единственный способ, гарантирующий корректную очистку всех ресурсов, принадлежавших потоку. При этом: 1) любые С++-объекты, созданные данным потоком, уничтожаются соответствующими деструкторами; 2) система корректно освобождает память, которую занимал стек потока; 3) система устанавливает код завершения данного потока (поддерживаемый объектом ядра «поток») — его и возвращает функция потока; 4) счетчик пользователей данного объекта ядра «поток» уменьшается на 1. 2.поток самоуничтожается вызовом функции ExitThread (нежелательный способ). Поток можно завершить принудительно, вызвав: VOID ExitThread(DWORD dwExitCode); При этом освобождаются все ресурсы операционной системы, выделенные данному потоку, но С++-ресурсы не очищаются. Именно поэтому лучше возвращать управление из функции потока, чем самому вызывать функцию ExitThread. В параметр dwExitCode помещается значение, которое система рассматривает как код завершения потока. Возвращаемого значения у этой функции нет, потому что после ее вызова поток перестает существовать. 3.один из потоков данного или стороннего процесса вызывает функцию TerminateThread (нежелательный способ). Вызов этой функции также завершает поток: BOOL TerminateThread( HANDLE hThread. DWORD dwExitCode); В отличие от ExitThread, которая уничтожает только вызывающий поток, эта функция завершает поток, указанный в параметре hThread. В параметр dwExitCode помещается значение, которое система рассматривает как код завершения потока. После того как поток будет уничтожен, счетчик пользователей его объекта ядра «поток» уменьшится на 1. 4.завершается процесс, содержащий данный поток (тоже нежелательно). Функции ExitProcess и TerminateProcess тоже завершают потоки. Единственное отличие в том, что они прекращают выполнение всех потоков, принадлежавших завершенному процессу. При этом гарантируется высвобождение любых выделенных процессу ресурсов, в том числе стеков потоков. Однако эти две функции уничтожают потоки принудительно — так, будто для каждого из них вызывается функция TerminateThread. А это означает, что очистка проводится некорректно: деструкторы С++-объектов не вызываются, данные на диск не сбрасываются и т. д.
11 Потоки и процессы как синхронизационные объекты.
Синхронизационный объект это такой объект который может быть использован как аргумент в одной из функций ожидания (wait function). • Синхронизационный объект может находиться в «сигнальном» или «несигнальном» состоянии. • Несколько процессов могут использовать один и тот же синхронизационный объект. • Доступ к синхронизационный объекту осуществляется через «handle».
Потоки и процессы как синхранизационные объекты : Process – сигнализирует о завершении процесса • Thread – сигнализирует о завершении потока
12 Синхронизация потоков с использованием критических секций.
Критическая секция — объект синхронизации в пользовательском режиме, позволяющий предотвратить попытку одновременного доступа к объектам. Объект-критическая секция помогает программисту выделить участок кода, где нить получает доступ к разделяемому ресурсу, и предотвратить одновременное использование ресурса. Перед использованием ресурса нить входит в критическую секцию (вызывает функцию EnterCriticalSection). Если после этого какая-либо другая нить попытается войти в ту же самую критическую секцию, ее выполнение приостановится, пока первая нить не покинет секцию с помощью вызова LeaveCriticalSection. Используется только для нитей одного процесса. Порядок входа в критическую секцию не определен. Существует также функция TryEnterCriticalSection, которая проверяет, занята ли критическая секция в данный момент. С ее помощью нить в процессе ожидания доступа к ресурсу может не блокироваться, а выполнять какие-то полезные действия. Пример. Синхронизация нитей с помощью критических секций. #include <windows.h> #include <stdio.h> CRITICAL_SECTION cs; int a[5]; HANDLE hThr; unsigned long uThrID;
void Thread( void* pParams ) { int i, num = 0; while (1) { EnterCriticalSection( &cs ); for (i=0; i<5; i++) a[i] = num; num++; LeaveCriticalSection( &cs ); } }
int main( void ) { InitializeCriticalSection( &cs ); hThr=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)Thread,NULL,0,&uThrID); while(1) { EnterCriticalSection( &cs ); printf("%d %d %d %d %d\n", a[0], a[1], a[2], a[3], a[4]); LeaveCriticalSection( &cs ); } return 0; }
13 Функция синхронизации WaitForSingleObject.
Wait-функции позволяют потоку в любой момент приостановиться и ждать освобождения какого-либо объекта ядра.
DWORD WaitForSingleObject (HANDLE hObject, DWORD dwMilliseconds), Когда поток вызывает эту функцию, первый параметр, hObject, идентифицирует объект ядра, поддерживающий состояния “свободен-занят”. Второй параметр, dwMilliseconds, указывает, сколько времени (в миллисекундах) поток готов ждать освобождения объекта. WaitForSingleObject(hProcess, INFINITE); В данном случае константа INFINITE, передаваемая во втором параметре, подсказывает системе, что вызывающий поток готов ждать этого события хоть целую вечность. Именно эта константа обычно и передается функции WaitForSingleObject, но Вы можете указать любое значение в миллисекундах. Кстати, константа INFINITE определена как 0xFFFFFFFF (или -1). Разумеется, передача INFINITE не всегда безопасна. Если объект так и не перейдет в свободное состояние, вызывающий поток никогда не проснется; одно утешение: тратить драгоценное процессорное время он при этом не будет. Возвращаемое значение функции WaitForSingleObject: • WAIT_OBJECT_0 -указанный объект находится в сигнальном состоянии (объект свободен); • WAIT_TIMEOUT - истек период ожидания. • WAIT_ABANDONED – мутекс не освобожден умершим потоком. • WAIT_FAILED – ошибка ,информацию об ошибке можно получить, вызвав функцию GetLastError.
14 Функция синхронизации WaitForMultipleObject.
Функция WaitForMultipleObjects аналогична WaitForSingleObject с тем исключением, что позволяет ждать освобождения сразу нескольких объектов или какого-то одного из списка объектов: DWORD WaitForMultipleObJects( DWORD dwCount, CONST HANDLE*phObjects, BOOL fWaitAll, DWORD dwMilliseconds); Параметр dwCount определяет количество интересующих объектов ядра. Его значение должно быть в пределах от 1 до MAXIMUM_WAIT_OBJECTS (в заголовочных файлах Windows оно определено как 64). Параметр phObjects — это указатель на массив описателей объектов ядра. WaitForMultipleObjects приостанавливает поток и заставляет его ждать освобождения либо всех заданных объектов ядра, либо одного из них. Параметр fWaitAll как раз и определяет, чего именно Вы хотите от функции. Если он равен TRUE, функция не даст потоку возобновить свою работу, пока не освободятся все объекты. Параметр dwMiUiseconds идентичен одноименному параметру функции WaitForSingleObject. Если Вы указываете конкретное время ожидания, то по его истечении функция в любом случае возвращает управление. И опять же, в этом параметре обычно передают INFINITE. Возвращаемое значение функции WaitForMultipleObjects сообщает, почему возобновилось выполнение вызвавшего ее потока. Значения WAIT_FAILED и WAIT_TIMEOUT никаких пояснений не требуют. Если Вы передали TRUE в параметре fWaitAll и все объекты перешли в свободное состояние, функция возвращает значение WAIT_OBJECT_0. Если же fWaitAll приравнен FALSE, она возвращает управление, как только освобождается любой из объектов. Вы, по-видимому, захотите выяснить, какой именно объект освободился. В этом случае возвращается значение от WAIT_OBJECT_0 до WAIT_OBJECT_0 + dwCount - 1. Иначе говоря, если возвращаемое значение не равно WAIT_TIMEOUT или WAIT_FAILED, вычтите из него значение WAIT_OBJECT_0, и Вы получите индекс в массиве описателей, на который указывает второй параметр функции WaitForMultipleObjects. Индекс подскажет Вам, какой объект перешел незанятое состояние. Если Вы передаете FALSE в параметре fWaitAll, функция WaitForMultipleObjects сканирует массив описателей (начиная с нулевого элемента), и первый же освободившийся объект прерывает ожидание. Это может привести к нежелательным последствиям. Например, Ваш поток ждет завершения трех дочерних процессов; при этом Вы передали функции массив с их описателями. Если завершается процесс, описатель которого находится в нулевом элементе массива, WaitPorMultipleObjects возвращает управление. Теперь поток может сделать то, что ему нужно, и вновь вызвать эту функцию, ожидая завершения другого процесса. Если поток передаст те же три описателя, функция немедленно вернет управление, и Вы снова получите значение WAIT_OB-JECT_0. Таким образом, пока Вы не удалите описатели тех объектов, об освобождении которых функция уже сообщила Вам, код будет работать некорректно.
15 Использование событий для синхронизации. События — самая примитивная разновидность объектов ядра. Они содержат счетчик числа пользователей (как и все объекты ядра) и две булевы переменные: одна сообщает тип данного объекта-события, другая — его состояние (свободен или занят). События просто уведомляют об окончании какой-либо операции. Объекты-события бывают двух типов: со сбросом вручную (manual-reset events) и с автосбросом (auto-reset events). Первые позволяют возобновлять выполнение сразу нескольких ждущих потоков, вторые — только одного. Объекты-события обычно используют в том случае, когда какой-то поток выполняет инициализацию, а затем сигнализирует другому потоку, что тот может продолжить работу. Инициализирующий поток переводит объект “событие” в занятое состояние и приступает к своим операциям. Закончив, он сбрасывает событие в свободное состояние. Тогда другой поток, который ждал перехода события в свободное состояние, пробуждается и вновь становится планируемым. Объект ядра “событие” создается функцией CreateEvent: HANDLE CreateEvent( PSECURITY_ATTRIBUTES psa, BOOL fManualReset, BOOL fInitialState, PCTSTR pszName); Параметр fManualReset (булева переменная) сообщает системе, хотите Вы создать событие со сбросом вручную (TRUE) или с автосбросом (FALSE). Параметр flnitialState определяет начальное состояние события — свободное (TRUE) или занятое (FALSE). После того как система создает объект-событие, CreateEvent возвращает описатель события, специфичный для конкретного процесса. Потоки из других процессов могут получить доступ к этому объекту: 1) вызовом CreateEvent с тем же параметром pszName; 2) наследованием описателя; 3) применением функции DuplicateHandle; и 4) вызовом OpenEvent с передачей в параметре pszName имени, совпадающего с указанным в аналогичном параметре функции CreateEvent. Вот что представляет собой функция OpenEvent. HANDLE OpenEvent( DWORD fdwAccess, BOOL fInherit, PCTSTR pszName); Ненужный объект ядра “событие” следует, как всегда, закрыть вызовом CloseHandle. Создав событие, можно напрямую управлять его состоянием. Чтобы перевести его в свободное состояние, нужно вызывать: BOOL SetEvent(HANDLE hEvent); А чтобы поменять его на занятое: BOOL ResetEvent(HANDLE hEvent); Для событий с автосбросом действует следующее правило. Когда его ожидание потоком успешно завершается, этот объект автоматически сбрасывается в занятое состояние. Для этого объекта обычно не требуется вызывать ResetEvent, поскольку система сама восстанавливает его состояние. Если событие со сбросом вручную заменить на событие с автосбросом, программа будет вести себя совершенно иначе. После вызова первичным потоком функции SetEvent система возобновит выполнение только одного из вторичных потоков. Какого именно — сказать заранее нельзя. Остальные два потока продолжат ждать.
№ 16 Использование семафоров для синхронизации Семафор - объекты ядра, используются для учета ресурсов, содержат счетчик числа пользователей, поддерживает два 32-битных значения со знаком: одно определяет максимальное число ресурсов (контролируемое семафором), другое используется как счетчик текущего числа ресурсов. Для семафоров определены следующие правила: 1. когда счетчик текущего числа ресурсов становится больше 0, семафор переходит в свободное состояние; 2. если этот счетчик равен 0, семафор занят; 3. система не допускает присвоения отрицательных значений счетчику текущего числа ресурсов; 4. счетчик текущего числа ресурсов не может быть больше максимального числа ресурсов. Семафор создается вызовом CreateSemaphore:
|