КАТЕГОРИИ:
АстрономияБиологияГеографияДругие языкиДругоеИнформатикаИсторияКультураЛитератураЛогикаМатематикаМедицинаМеханикаОбразованиеОхрана трудаПедагогикаПолитикаПравоПсихологияРиторикаСоциологияСпортСтроительствоТехнологияФизикаФилософияФинансыХимияЧерчениеЭкологияЭкономикаЭлектроника
|
Синхронизирующие объекты ОС
Механизмы синхронизации, основанные на использовании глобальных переменных процесса, не подходят для синхронизации потоков разных процессов. В этом случае операционная система должна предоставлять потокам такие объекты синхронизации, которые были бы видны для всех потоков, даже если они принадлежат различным процессам и работают в разных адресных пространствах. Примерами таких синхронизирующих объектов являются системные семафоры, мьютексы, события, таймеры, а также такие объекты ОС, как файлы, потоки, процессы. Объекты синхронизации всегда находятся в двух состояниях – сигнальном и свободном (несигнальном). С помощью этих состояний происходит приостановка и активация потоков. Принцип работы синхронизирующих объектов в ОС следующий. Потоки с помощью специального системного вызова сообщают операционной системе о том, что они хотят синхронизировать свое выполнение с состоянием некоторого объекта. Поток, выполнивший системный вызов, переводится операционной системой в состояние ожидания до тех пор, пока синхронизирующий объект не перейдет в сигнальное состояние. При этом поток может ожидать установки сигнального состояния не одного объекта, а нескольких. В зависимости от объекта синхронизации в состояние готовности могут переводиться либо все ожидающие это событие потоки, либо один из них. Необходимо отметить, что синхронизация тесно связана с планированием потоков с учетом правил предоставления приоритетов, принятых в операционной системе. Как можно приостановить работу потока? Это можно сделать с помощью Wait-функций. Например, функция Sleep() приостанавливает работу потока на заданное число миллисекунд. Если в качестве аргумента вы укажите 0 мс, то произойдет следующее. Поток откажется от своего кванта процессорного времени, однако тут же появится в списке потоков готовых к выполнению. Иными словами произойдет намеренное переключение потоков. (Вернее сказать, попытка переключения. Ведь следующим для выполнения потоком вполне может стать тот же самый.) Функция WaitForSingleObject() приостанавливает выполнение потока до тех пор, пока не произойдет одно из двух событий: - истечет таймаут ожидания; - ожидаемый объект перейдет в сигнальное (signaled) состояние. По возвращаемому значению можно понять, какое из двух событий произошло. Ожидать с помощью wait-функций можно большинство объектов ядра, например, объект "процесс" или "поток", чтобы определить, когда они завершат свою работу. Функции WaitForMultipleObjects передается сразу массив объектов. Можно ожидать срабатывания сразу всех объектов или какого-то одного из них. Пример 3. Программа создает два одинаковых потока и ожидает их завершения. }
Мьютекс, как и семафор, обычно используется для управления доступом к данным. Мьютекс – это переменная, которая может находиться только в двух состояниях: занятом и свободном. Это позволяет с помощью мьютекса защищать какой-либо единичный ресурс от единовременного обращения к нему различных потоков. Работа мьютекса хорошо поясняется в терминах «владения». Предположим, что мьютекс находится в сигнальном (свободном) состоянии. Тогда поток, который пытается получить доступ к критическим данным, тут же становится его владельцем, устанавливая его в несигнальное (занятое) состояние, и входит в критическую секцию. После того как поток выполнил работу с критическими данными, он устанавливает мьютекс в сигнальное состояние. В этот момент мьютекс опять свободен и не принадлежит ни одному потоку. Если какой-либо поток ждет его освобождения, то он становится следующим владельцем этого мьютекса, опять переключая его состояние.
Пример 4. Допустим, в программе используется ресурс, например, файл или буфер в памяти. Функция WriteToBuffer() вызывается из разных потоков. Чтобы избежать коллизий при одновременном обращении к буферу из разных потоков, используем мьютекс. Прежде чем обратиться к буферу, ожидаем освобождения мьютекса. HANDLE hMutex;int main(){ hMutex = CreateMutex( NULL, FALSE, NULL); // Создаем мьютекс в свободном состоянии ... // Создание потоков, и т.д. ...}BOOL WriteToBuffer(){ DWORD dwWaitResult; // Ждем освобождения мьютекса перед тем как обратиться к буферу. dwWaitResult = WaitForSingleObject( hMutex, 5000L); // 5 секунд на таймаут if (dwWaitResult == WAIT_TIMEOUT) // Таймаут. Мьютекс за это время не освободился. { return FALSE; } else // Мьютекс освободился, и наш поток его занял. Можно работать. { Write_to_the_buffer(). ... ReleaseMutex(hMutex); // Освобождаем мьютекс. } return TRUE;}
События (Event), также как и мьютексы имеют два состояния - установленное и сброшенное. События бывают со сбросом вручную и с автосбросом. Когда поток дождался (wait-функция вернула управление) события с автосбросом, такое событие автоматически сбрасывается. В противном случае событие нужно сбрасывать вручную, вызвав функцию ResetEvent(). Допустим, сразу несколько потоков ожидают одного и того же события, и событие сработало. Если это было событие с автосбросом, то оно позволит работать только одному потоку (ведь сразу же после возврата из его wait-функции событие сбросится автоматически!), а остальные потоки останутся ждать. Если же это было событие со сбросом вручную, то все потоки получат управление, а событие так и останется в установленном состоянии, пока какой-нибудь поток не вызовет ResetEvent(). Пример 5. Программа непрерывно в цикле производит какие-то вычисления. Нужно иметь возможность приостановить на время ее работу. Допустим, это просмотрщик видео файлов, который в цикле, кадр за кадром отображает информацию на экран. Не будем вдаваться в подробности видео функций. Реализуем функции Pause и Play для программы. Используем событие со сбросом вручную. // Главная функция потока, которая в цикле отображает кадрыunsigned __stdcall VideoThreadFunc( void * arg){ while (bSomeCondition) { WaitForSingleObject(m_hPauseEvent,INFINITE); // Если событие сброшено, ждем ... // Отображаем очередной кадр на экран } _endthreadex( 0 ); return 0;};void Play(){ SetEvent(m_hPauseEvent);};void Pause(){ ResetEvent(m_hPauseEvent);};
|