Мегаобучалка Главная | О нас | Обратная связь


Управление потоками. Средства синхронизации потоков



2019-07-03 425 Обсуждений (0)
Управление потоками. Средства синхронизации потоков 0.00 из 5.00 0 оценок




Поток (thread) – это основной компонент вычислительного процесса в системе, которому ОС выделяет машинное время. Поток может выполнять какую–то часть общего кода процесса, в том числе и ту часть, которая в это время уже выполняется другим потоком. Все потоки одного процесса пользуются ресурсами породившего их процесса. Кроме того, каждому потоку система и/или программист приписывает приоритет выполнения и набор структур, описывающих контекст потока. ОС использует их для запоминания контекста потока, когда его выполнение прерывается. Потоки подобны процессам, но требуют меньших затрат при своем создании. Они в меньшей степени, чем процессы, защищены друг от друга, но позволяют совместить выполнение операций и выиграть в общей производительности процесса. Типичным приложением со многими потоками является сервер, обслуживающий многих пользователей. Каждый новый пользователь обслуживается отдельным потоком одного процесса. Вместо ожиданий, которые связаны с дисковыми операциями, система может перейти к выполнению другого потока.

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

Создание процессов с множеством потоков требует тщательного предварительного анализа с тем, чтобы должным образом скоординировать действия операционной системы и других программных компонентов. Отслеживание состояний многочисленных потоков требует значительных временных затрат, поэтому следует помнить, что Win32 API предоставляет и другие средства реализации асинхронного выполнения операций. Например: асинхронный ввод–вывод, функции ожидания системных событий (wait functions) и другие возможности.

Перечислим наиболее типичные случаи, когда следует применять многопоточность:

· управление вводом в различные документы. Ввод данных каждого документа приложения организован в виде отдельного потока;

· управление вводом данных из нескольких устройств телекоммуникации;

· разграничение приоритетов выполнения задач. Потокам, требующим высокой скорости реакции, присваивается высокий приоритет, а другим потокам более низкий;

· снижение времени реакции на действия пользователя по вводу данных при одновременном выполнении фоновых вычислений.

 

Реализация многозадачности в виде одного процесса с несколькими потоками является обычно более эффективной, чем в виде многих процессов с одним потоком по следующим причинам:

· контексты потоков занимают меньший объем, чем контексты процессов, и система переключает их быстрее;

· взаимодействие потоков проще, так как они могут пользоваться глобальными переменными в общем для них адресном пространстве процесса;

· потоки одного процесса легче синхронизировать, так как им доступны описатели объектов ядра из общего контекста процесса.

 

Рассмотрим методы синхронизации потоков одного или нескольких процессов. Все методы основаны на создании специальных объектов синхронизации. У этих объектов два состояния: установлен и сброшен. Совместный доступ потоков к разделяемым ресурсам: описателям файлов, портов, глобальным переменным – может создать конфликты. Например, один поток читает данные, а другой пытается одновременно их изменить, или один поток ждет завершения определенной операции другим потоком, а тот, в свою очередь, ждет отработки первого потока. Такое зацикливание называется тупиковой ситуацией (deadlock). В любом случае синхронизация состоит в том, что поток переводит себя в состояние блокированного до наступления какого–либо события.

Для перевода процесса в состояние блокировки используются функции ожидания: WaitForMultipleObjects(), WaitForMultipleObjectsEx(), WaitForSingleObject (), WaitForSingleObjectEx(). При вызове этих функций им передается указатель на объект синхронизации. Объект проверяется. Если он не установлен, то функция будет ждать до тех пор, пока не истечет тайм–аут. Все это время поток будет блокирован. Функции, у которых в имени есть Single, предназначены для установки одного синхронизирующего объекта, а где есть Multiple, можно установить ожидание сразу нескольким объектам. Функции с префиксами Msg предназначены для ожидания события определенного типа, например ввода с клавиатуры. Функции с окончанием Ex расширены опциями по управлению асинхронным вводом–выводом.

Выход процесса из состояния блокировки происходит при наступлении ожидаемого события. Событие состоит в изменении состояния какого–либо объекта. Практически любые объекты ядра (процессы, файлы, транспортеры и т.д.) могут выступать в роли синхронизирующих. Cуществуют объекты, предназначенные именно для синхронизации потоков: критические секции (critical section), мьютексы (mutex – mutually exclusive – взаимное исключение), семафоры (semaphore). Теория синхронизации процессов/потоков разработана голландским профессором математики Э. Дейкстрой в начале 70 годов.

Критические секции. Механизм критических секций может использоваться только в рамках одного процесса. Объект, представляющий критическую секцию, может использоваться только одним потоком в данный момент времени, что делает использование критических секций крайне полезными при разграничении доступа к общим ресурсам.

Для работы с критической секцией необходимо объявить переменную типа CRITICAL_SECTION и инициализировать ее вызовом функции InitializeCriticalSection(). Теперь все потоки могут использовать функции EnterCriticalSection() и LeaveCriticalSection() для ограждения критического раздела кода. Когда критический раздел становится ненужным, то его необходимо удалить.

Мьютексы. Мьютекс (взаимоисключение, mutex) – это объект синхронизации, который устанавливается в особое сигнальное состояние, когда не занят каким–либо потоком. Только один поток владеет этим объектом в любой момент времени, отсюда и название таких объектов – одновременный доступ к общему ресурсу исключается. Например, чтобы исключить запись двух потоков в общий участок памяти в одно и то же время, каждый поток ожидает, когда освободится мьютекс, становится его владельцем и только потом пишет что–либо в этот участок памяти. После всех необходимых действий мьютекс освобождается. Таким образом, мьютекс установлен в тот момент, когда ресурс свободен. Если к объекту есть доступ, то говорят, что мьютекс сброшен.

Для синхронизации потоков одного процесса более эффективным подходом является использование критических секций. Мьютексы выполняются медленнее критических разделов, однако обладают большими возможностями, чем критические разделы. Так, например, они могут использоваться разными процессами.

Создаются мьютексы с помощью API–функции CreateMutex(). Для доступа к объекту Mutex используется ожидающая функция WaitForSingleObject (). Освобождение ресурса осуществляется с помощью функции ReleaseMutex ().

Синхронизация потоков с помощью объектов Mutex выглядит так: один из потоков порождает объект Mutex и присваивает ему глобальное имя при помощи функции CreateMutex(). Остальные потоки могут использовать функцию OpenMutex() для получения описателя объекта. Перед входом в критическую секцию поток вызывает одну из функций ожидания, передавая ей в качестве параметра описатель объекта Mutex. Если объект Mutex уже захвачен, то поток блокируется до освобождения объекта. Если не захвачен, то исполнение потока продолжается, а объект Mutex захватывается.

Два (или более) процесса могут создать мьютекс с одним и тем же именем, вызвав метод CreateMutex(). Первый процесс создает мьютекс, а следующие получают хэндл уже существующего объекта. Это дает возможность нескольким процессам получить хэндл одного и того же мьютекса, освобождая программиста от необходимости заботиться о том, кто в действительности создает мьютекс. Если используется такой подход, желательно установить флаг bInitialOwner в false, иначе возникнут определенные трудности при определении действительного создателя мьютекса.

Несколько процессов могут получить хэндл одного и того же мьютекса, что делает возможным взаимодействие между процессами. Можно использовать следующие механизмы такого подхода:

 дочерний процесс, созданный при помощи функции CreateProcess(), может наследовать хэндл мьютекса в случае, если при его (мьютекса) создании функцией CreateMutex() был указан параметр lpMutexAttributes .

 процесс может получить дубликат существующего мьютекса с помощью функции DuplicateHandle ().

 процесс может указать имя существующего мьютекса при вызове функций OpenMutex() или CreateMutex().

Семафоры. Данный объект синхронизации позволяет ограничить доступ потоков к объекту синхронизации на основании их количества (например, мы хотим, чтобы к какому–нибудь объекту могли обратиться максимум 3 потока). Сначала семафор инициализируется, и ему передается количество потоков, которые к нему могут обратиться. Дальше при каждом обращении к ресурсу его счетчик уменьшается. Когда счетчик уменьшится до 0, к ресурсу обратиться больше нельзя. При отсоединении потока от семафора его счетчик увеличивается, что позволяет другим потокам обратиться к нему. Сигнальному состоянию соответствует значение счетчика больше нуля. Когда счетчик равен нулю, семафор считается неустановленным (сброшенным).

Существует несколько видов семафоров: семафор событий, взаимного исключения, взаимного ожидания и именованный. Когда семафор событий находится в нерабочем состоянии, операционная система блокирует исполнение всех процессов, которые запрашивают состояние семафора. Семафоры взаимного исключения предотвращают возникновение тупиков при обращении к разделяемым ресурсам. Операционная система блокирует доступ к разделяемому ресурсу до тех пор, пока соответствующий семафор не будет свободен. При использовании разделяемого ресурса система устанавливает значение семафора в рабочее состояние, показывая тем самым, что ресурс занят. Семафор взаимного ожидания представляет собой пакет из семафоров взаимного исключения или семафоров событий. Система может приостановить процесс до тех пор, пока один или все семафоры внутри именованного семафора не окажутся в состоянии «свободен» (в зависимости от потребности процесса). Реакция на именованный семафор зависит от процессов, совместно использующих его.

Для создания семафора используется функция CreateSemaphore(). Эта функция создает семафор с заданным начальным значением счетчика и максимальным значением. Это значение ограничивает доступ. Функция OpenSemaphore() осуществляет доступ к семафору. Функция ReleaseSemaphore() увеличивает значение счетчика. Счетчик может меняться от 0 до максимального значения. После завершения работы достаточно вызвать CloseHandle().

События. События – самая простая разновидность синхронизирующих объектов. Они полезны в тех случаях, когда необходимо послать сообщение потоку, что произошло определенное событие. Например, при асинхронных операциях ввода и вывода из одного устройства система устанавливает событие в сигнальное состояние, когда заканчивается какая–либо из этих операций. Один поток может использовать несколько различных событий в нескольких перекрывающихся операциях, а затем ожидать прихода сигнала от любого из них. Таким образом, смысл использования события состоит в уведомлении одного или нескольких ожидающих потоков.

Поток может использовать функцию CreateEvent () для создания объекта «событие». Создающий событие поток устанавливает его в начальное состояние. В создающем потоке можно указать имя события. Потоки других процессов могут получить доступ к этому событию по имени, указав его в функции OpenEvent(). Объекты–события бывают двух типов «со сбросом вручную» и «с автоматическим сбросом». Разница между ними в том, что первый вид события нужно применять, если событие ждут несколько потоков. Только сброс вручную позволяет это сделать. В противном случае первый же обработчик сбросит событие, и другие потоки об этом и не узнают.

Объект «событие» может находиться в двух состояниях: «занят» (non–signaled) и «свободен» (signaled). Для перевода объекта «событие» в свободное состояние используется функция SetEvent(), а для перевода в занятое – ResetEvent(). С помощью любой из функций ожидания можно перевести вызвавший поток в состояние блокировки до освобождения объекта «событие». Если этот объект является объектом «с автоматическим сбросом» то функция ожидания автоматически переведет его в состояние занятого. В данном случае в отличие от всех других объектов синхронизации потоков может быть любое количество.

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

Рассмотрим, какие механизмы в операционных системах реального времени делают систему реального времени предсказуемой.

Система приоритетов и алгоритмы диспетчеризации. В задачах реального времени параллельно выполняемые задачи стараются максимально компоновать в виде потоков, исполняющихся в пределах одного процесса. Потоки, принадлежащие одному процессу, разделяют его адресное пространство, поэтому они могут легко обмениваться данными; также время переключения между такими потоками (время, за которое процессор переходит от выполнения команд одного потока к выполнению команд другого) оказывается значительно меньшим, чем время переключения между процессами.

Механизмы межпроцессного взаимодействия. Другой набор механизмов реального времени относится к средствам синхронизации процессов и передачи данных между ними. Для операционных систем реального времени характерна развитость этих механизмов. К таким механизмам относятся: семафоры, мьютексы, события, сигналы, средства для работы с разделяемой памятью, каналы данных, очереди сообщений. Многие из подобных механизмов используются и в ОС общего назначения, но их реализация в операционных системах реального времени имеет свои особенности – время исполнения системных вызовов почти не зависит от состояния системы, в каждой операционной системе реального времени есть по крайней мере один быстрый механизм передачи данных от процесса к процессу.

 



2019-07-03 425 Обсуждений (0)
Управление потоками. Средства синхронизации потоков 0.00 из 5.00 0 оценок









Обсуждение в статье: Управление потоками. Средства синхронизации потоков

Обсуждений еще не было, будьте первым... ↓↓↓

Отправить сообщение

Популярное:
Как распознать напряжение: Говоря о мышечном напряжении, мы в первую очередь имеем в виду мускулы, прикрепленные к костям ...
Как выбрать специалиста по управлению гостиницей: Понятно, что управление гостиницей невозможно без специальных знаний. Соответственно, важна квалификация...
Организация как механизм и форма жизни коллектива: Организация не сможет достичь поставленных целей без соответствующей внутренней...



©2015-2024 megaobuchalka.ru Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. (425)

Почему 1285321 студент выбрали МегаОбучалку...

Система поиска информации

Мобильная версия сайта

Удобная навигация

Нет шокирующей рекламы



(0.011 сек.)