Многозадачность (Операционные Системы)

Материал из Национальной библиотеки им. Н. Э. Баумана
Последнее изменение этой страницы: 16:23, 24 августа 2017.

Многозада́чность (англ. multitasking) — свойство операционной системы или среды выполнения обеспечивать возможность параллельной (или псевдопараллельной) обработки нескольких процессов. Истинная многозадачность операционной системы возможна только в распределённых вычислительных системах. Иными словами, многозадачность - способ выполнения нескольких задач в один период времени. При этом задачи делят между собой общие ресурсы (resources sharing), помимо этого осуществляется планирование (scheduling).[1]

Система называется однозадачной,если она не обладает свойством многозадачности, т.е. задачи в ней выполняются последовательно.

DOS - одноза­дачная операционная система, в ней после запуска программы на выполнение больше ничего другого выполняться не может (не считая время от времени вызываемых обработ­чиков прерываний). Многозадачность имеет место при наличии нескольких потоков исполнения.
Поток исполнения - набор последовательных инструкций, выполняемых процессором во время работы программы. На одном процессорном ядре одновременно выполняется лишь один поток исполнения, который называется активным. Процесс выбора активного исполнительного потока носит название планирование.

Содержание

Свойства многозадачной среды

Примитивные многозадачные среды обеспечивают чистое «разделение ресурсов», когда за каждой задачей закрепляется определённый участок памяти, и задача активизируется в строго определённые интервалы времени.

Более развитые многозадачные системы проводят распределение ресурсов динамически, когда задача стартует в памяти или покидает память в зависимости от её приоритета и от стратегии системы. Такая многозадачная среда обладает следующими особенностями:

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

Трудности реализации многозадачной среды

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

История многозадачных операционных систем

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

В дальнейшем, после появления нескольких удачных решений, многозадачные среды стали совершенствоваться, и в настоящее время употребляются повсеместно.

Впервые многозадачность операционной системы была реализована в ходе разработки операционной системы Multics (1964 год). Одной из первых многозадачных систем была OS/360 (1966), используемая для компьютеров фирмы IBM и их советских аналогов ЕС ЭВМ. Разработки системы были сильно затянуты, и на начальное время фирма IBM выдвинула однозадачный DOS, чтобы удовлетворить заказчиков до полной сдачи OS/360 в эксплуатацию. Система подвергалась критике по причине малой надёжности и трудности эксплуатации.

В 1969 году на основе Multics была разработана система UNIX с достаточно аккуратным алгоритмическим решением проблемы многозадачности. В настоящее время на базе UNIX созданы десятки операционных систем.

На компьютерах PDP-11 и их советских аналогах СМ-4 использовалась многозадачная система RSX-11 (советский аналог — ОСРВ СМ ЭВМ), и система распределения времени TSX-PLUS, обеспечивающая ограниченные возможности многозадачности и многопользовательский режим разделения времени, эмулируя для каждого пользователя однозадачную RT-11 (советский аналог — РАФОС). Последнее решение было весьма популярно из-за низкой эффективности и надёжности полноценной многозадачной системы.

Аккуратным решением оказалась операционная система VMS, разработанная первоначально для компьютеров VAX (советский аналог — СМ-1700) как развитие RSX-11.

Первый в мире мультимедийный персональный компьютер Amiga 1000 (1984 год) изначально проектировался с расчётом на полную аппаратную поддержку вытесняющей многозадачности реального времени в ОС AmigaOS. В данном случае разработка аппаратной и программной части велась параллельно, это привело к тому, что по показателю квантования планировщика многозадачности (1/50 секунды на переключение контекста) AmigaOS долгое время оставалась непревзойдённой на персональных компьютерах.

Многозадачность обеспечивала также фирма Microsoft в операционных системах Windows. При этом Microsoft выбрала две линии разработок — на базе приобретённой ею Windows 0.9, которая после долгой доработки системы, изначально обладавшей кооперативной многозадачностью, аналогичной Mac OS, вылилась в линейку Windows 3.x, и на основе идей, заложенных в VMS, которые привели к созданию операционных систем Windows NT. Использование опыта VMS обеспечило системам существенно более высокую производительность и надёжность. По времени переключения контекста многозадачности (квантование) только эти операционные системы могут быть сравнимы с AmigaOS и UNIX (а также его потомками, такими, как ядро Linux).

Интересно, что многозадачность может быть реализована не только в операционной, но и языковой среде. Например, спецификации языков программирования Modula-2 и Ada требуют поддержки многозадачности вне привязки к какой-либо операционной системе. В результате, популярная в первой половине 1990-х годов реализация языка программирования TopSpeed Модула-2 от JPI/Clarion позволяляла организовывать различные типы многозадачности (кооперативную и вытесняющую — см. ниже) для потоков одной программы в рамках такой принципиально однозадачной операционной системы, как MS-DOS. Это осуществлялось путём включения в модуль программы компактного планировщика задач, содержащего обработчик таймерных прерываний . Языки программирования, обладающие таким свойством, иногда называют языками реального времени.

Классификация

По типу наименьшего элемента управляемого кода

Процессная многозадачность.

Здесь программа — наименьший элемент управляемого кода, которым может управлять планировщик операционной системы. Известна большинству пользователей (одновременная работа в текстовом редакторе и прослушивание музыки). Многозадачная система позволяет двум или более программам выполняться одновременно.

Процесс — это понятие, относящееся к операционной системе. Каждый раз при запуске приложения создается и запускается новый процесс. С каждым процессом связаны следующие ресурсы, как:

  • виртуальное адресное пространство;
  • исполнимый код и данные;
  • базовый приоритет;
  • описатели объектов;
  • переменные окружения.

Поточная многозадачность.

Многопоточность — специализированная форма многозадачности. Наименьший элемент управляемого кода — поток. Многопотоковая (multi-threaded) система предоставляет возможность одновременного выполнения одной программой 2 и более задач (потоков). Поток (thread) — это основной элемент системы, которому ОС выделяет машинное время. Поток может выполнять какую-то часть общего кода процесса, в том числе и ту часть, которая в это время уже выполняется другим потоком.

Потоки и процессы:

  • Поток определяет последовательность исполнения кода в процессе. 

  • Процесс ничего не исполняет, он просто служит контейнером потоков.

  • Потоки всегда создаются в контексте какого-либо процесса, и вся их жизнь проходит только в его границах.

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

По способу организации времени выполнения каждого процесса

Параллельная многозадачность

Идеальным случаем многозадачности является параллельная многозадачность, когда каждая задача исполняется в своём аппаратном микропроцессорном ядре действительно одновременно друг с другом. Истинная многозадачность операционной системы возможна только в распределенных вычислительных системах. Реализация данного типа многозадачности требует больших материальных вложений (требуется отдельное аппаратное средство для каждой задачи), поэтому обычно её применение не является целесообразным. Ниже представлен пример системы , в которой реализована параллельная многозадачность.

Микроконтроллер Propeller от Parallax обеспечивает работу сразу 8 задач.Восемь встроенных процессоров-ядер, могут выполнять как совместные, так и независимые задачи, получая доступ к разделяемым ресурсам посредством центрального переключающего устройства. Разработчик полностью контроллирует, как и когда загружать каждое из ядер; ни компилятор, ни какая-либо операционная система не распределяет задачи между несколькими ядрами. Общий источник тактовой частоты обеспечивает всем процессорам единую временную базу и синхронизацию. Контроллер имеет два возможных языка программирования: легкий для освоения высокоуровневый язык Spin, а так же низкоуровневый Propeller Ассемблер.[2]

Propeller Parallax

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

Типы псевдопараллельной многозадачности

Невытесняющая многозадачность

16-и разрядная Windows уже стала поддерживать и вытесняющую многозадачность (non-preemptive multitasking). Такой тип многозадачности стал возможен благодаря основанной на сообщениях архитектуре Windows.Windows – программа может находиться в памяти и не выполняться до тех пор, пока не получила сообщение. Ранее, эти сообщения часто являлись прямым или косвенным результатом ввода информации пользователем с клавиатуры или мыши. Сейчас, механизм посылки сообщений широко используется и для обмена данными, инициации какого-либо действия приложения и т.п.

При всем при этом, Windows использовала вытесняющую многозадачность для выполнения DOS – программ, а также позволяла некоторым программам, например, для целей мультимедиа, получать аппаратные прерывания от таймера.

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

Частично преодолеть последнее ограничение, можно, опять таки используя таймер для периодической смене выполняемых программ. Другим решением по преодолению невытесняющей многозадачности является вызов функции PeekMessage. Обычно программа использует вызов функции GetMessage для извлечения сообщений из очереди. Однако, если в данный момент времени очередь сообщений пуста, то функция GetMessage будет ждать поступления сообщения в очередь, а затем возвратит его. Функция PeekMessage работает иначе – она возвращает управление программе даже в том случае, если сообщений в очереди нет. Таким образом, выполнение работы, требующей больших затрат времени, будет продолжаться до того момента, пока в очереди не появятся сообщения для данной или любой другой программы.

Плюсы Минусы
простота реализации затруднена оперативная реакция на внешние события
предсказуемость в поведении, исключение некоторых нежелательных ситуаций, таких как голодание (starvation),гонка (race condition) незащищенность задач друг от друга, снижение надёжности системы — как правило одна неправильно работающая задача блокирует или нарушает работу остальных
минимальные требования к системе и минимизация затрат ресурсов
Совместная или кооперативная многозадачность

Первые операционные системы, реализованные на персональных компьютерах, сильно уступали в концептуальном плане и по своим реальным возможностям системам с разделением времени, давно реализованным в mainframe- компьютерах. В Win 16, например, тоже существует понятие многозадачности. Реализовано оно следующим образом: обработав очередное сообщение, приложение передает управление операционной системе, которая может передать управление другому приложению. Такой вид многозадачности, при котором операционная система передает управление от одного приложения другому не в любой момент времени, а только когда текущее приложение отдает управление системе, получил, как было упомянуто, название кооперативной многозадачности (cooperative multi-tasking).

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

В Windows начиная с Windows 95 реализован принципиально другой вид многозадачности, в котором операционная система действительно контролирует и управляет процессами, потоками и их переключением. Способность операционной системы прервать выполняемый поток практически в любой момент времени и передать управление другому ожидающему потоку определяется термином preemptive multitasking - преимущественная, или вытесняющая, многозадачность. Реализация ее выглядит так: все существующие в данный момент потоки, часть из которых может принадлежать одному и тому же процессу, претендуют на процессорное время и, с точки зрения пользователя должны выполняться одновременно. Для создания этой иллюзии система через определенные промежутки времени забирает управление, анализирует свою очередь сообщений, распределяет сообщения по другим очередям в пространстве процессов и, если считает нужным, переключает потоки.

Схема обработки прерывания следующая:

  1. Работа основной программы пользователя.
  2. Возникновение прерывания.
  3. Сохранение параметров работающей программы (регистров процессора).
  4. Переход по адресу процедуры обработки прерывания.
  5. Выполнение процедуры обработки прерывания.
  6. Восстановление параметров работающей программы.
  7. Переход по адресу следующей команды основной программы.

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

Реализация вытесняющей многозадачности в Windows 2000 дает не только возможность плавного переключения задач, но и устойчивость среды к зависаниям, так как ни одно приложение не может получить неограниченные права на процессорное время и другие ресурсы. Так система создает эффект одновременного выполнения нескольких приложений. Если компьютер имеет несколько процессоров, то системы Windows NT/2000 могут действительно совмещать выполнение нескольких приложений. Если процессор один, то совмещение остается иллюзией. Когда заканчивается квант времени, отведенный текущей программе, система ее прерывает, сохраняет контекст и отдает управление другой программе, которая ждет своей очереди. Величина кванта времени (time slice) зависит от ОС и типа процессора, в Windows NT она в среднем равна 20 мс. Следует отметить, что добиться действительно одновременного выполнения потоков можно только на машине с несколькими процессорами и только под управлением Windows NT/2000, ядра которых поддерживают распределение потоков между процессорами и процессорного времени между потоками на каждом процессоре. Windows 95 работает только с одним процессором. Даже если у компьютера несколько процессоров, под управлением Windows 95 задействован лишь один из них, а остальные простаивают. [3].

Q3YPJSX0HV0.jpg

Плюсы Минусы
переключение между задачами полностью возложено на диспетчер задач, и задача выполняется как в однозадачном окружении(пока от задачи не требуется взаимодействие с другими задачами или доступ к общим ресурсам) требуются специальные методы синхронизации задач друг с другом во избежание неопределённых ситуаций и конфликтов
задача может оперативно получить управление в случае необходимости вне зависимости от состояния других задач, что обеспечивает работу в реальном времени требуется поддержка со стороны аппаратного обеспечения (как минимум для сохранения и восстановления состояния микропроцесора при переключении)
возможности реализации защиты операционной системой задач от ошибок и атак требует больше системных ресурсов (память под стек и сохранение состояния для каждого процессора и время на переключение контекстов)

Диспетчеризация

Эффективность многозадачной системы во многом зависит от способа диспетчеризации задач на исполнение.

Состояние задачи

Для понимания диспетчеризации важным является понятие состояния задачи. Основные состояния задач такие:

  • выполняется;
  • готова к исполнению;
  • заблокирована.

Задача находится в состоянии исполнения тогда, когда под неё выделен ресурс процессора и передано управление. Очевидно, что в системе не может быть выполняющихся задач больше, чем физических процессоров. Поэтому существует второе состояние — готовность к исполнению. Задача в это состояние тогда, когда она может исполняться, то есть не существует причин, вызывающих блокировку, но вычислительные ресурсы отданы под другие задачи. В третье состояние задача попадает при невозможности её исполнения в данный момент времени.

  • ожидание какого либо события ;
  • незавершённая инициализация (когда ещё не созданы другой задачей или системой начальные условия для запуска);
  • «заморозка» (явная остановка исполнения, обычно в отладочных целях);
  • завершение (состояние , когда исполнение уже закончено, но ресурсы ещё не освобождены);
  • специфические случаи.

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

Стратегии диспетчеризации

Известно несколько основных стратегий диспетчеризации:

  • круговая (round-robin).Управление между задачами производится в в каком-либо фиксированном порядке через определённые промежутки времени;
  • случайная (random).Через каждый определённый промежуток времени управление передаётся на произвольную задачу;
  • приоритетная (priority-driven).Каждая задача имеет уровень приоритета и управление получает незаблокированная задача имеющая наибольший уровень;
  • управляемая дедлайнами (deadline-driven). Передача управления производится в порядке, обеспечивающем выполнение задач в определённый срок.

Программная реализация многозадачности

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

Запустить процесс можно как при помощи командной строки системного меню Windows, при помощи программы Проводника, так и программно, из другого приложения. Это можно выполнить при помощи функций API:

Для 16-битных приложений используется функция WinExec:

UINT WinExec(

LPCSTR lpCmdLine, // command line

UINT uCmdShow// window style
);

Первый параметр является командной строкой, в которой указывается имя файла и параметры, указываемые после имени загружаемого файла.

После запуска, программа начинает параллельную работу относительно приложения-родителя и ее выполнение не зависит от других приложений, если, конечно, зависимость не предусмотрена самой природой приложений.

В среде Windows 32 следует использовать другой способ порождения процессов:

BOOL CreateProcess(

LPCTSTR lpApplicationName,	// pointer to name of executable module

LPTSTR lpCommandLine,	// pointer to command line string

LPSECURITY_ATTRIBUTES lpProcessAttributes,	// pointer to process security attributes

LPSECURITY_ATTRIBUTES lpThreadAttributes,	// pointer to thread security attributes

BOOL bInheritHandles,	// handle inheritance flag

DWORD dwCreationFlags,	// creation flags

LPVOID lpEnvironment,	// pointer to new environment block

LPCTSTR lpCurrentDirectory,	// pointer to current directory name

LPSTARTUPINFO lpStartupInfo,	// pointer to STARTUPINFO

LPPROCESS_INFORMATION lpProcessInformation // pointer to PROCESS_INFORMATION

);

Первый параметр является указателем на имя запускаемого файла. Имя может содержать полный путь к файлу (диск:\каталог\…\файл). Если имя не содержит пути, то операционная система ищет файл в текущем каталоге, затем в системных каталогах и в каталогах, указанных в разделе PATH при загрузке системы.

Второй параметр указывает на командную строку.

Третий и четвертый параметры определяют, может ли возвращаемый указатель процесса наследоваться дочерними процессами и потоками.

bInheritHandles и dwCreationFlags содержит дополнительные флаги управления созданием и приоритетом процесса.

lpEnvironment содержит указатель на буфер памяти, в котором будет создаваться служебная информация по процессу. Если равен NULL, то операционная система сама отводит место в памяти под эту информацию.

lpCurrentDirectory указатель на строку, содержащую путь к каталогу, который будет использоваться запускаемым процессом как текущий. Если значение поля равно , то текущей директорией будет считаться каталог, являющийся текущим для процесса-родителя.

lpStartupInfo определяет структуру STARTUPINFO , которая описывает окно, создаваемое для запускаемого процесса (в ней содержится информация, похожая на ту, которая передается в процедуру CreateWindow).

lpProcessInformation указатель на структуру, заполняемую после создания нового процесса. Структура содержит информацию о созданном процессе.

Многопоточность в программе реализовать можно несколькими путями.

Непосредственное использование системного таймера для указание процедуры, вызываемой периодически. Данный способ был рассмотрен в лекции "Использование Таймера". Это типичный представитель вытесняющей многозадачности. К достоинствам этого способа можно отнести то, что программа может устанавливать и изменять период вызова функции.

Использование системного таймера для организации посылки синхронных сообщений выбранному окну (порядок организации описан в лекции "Использование Таймера"). С помощью данного способа реализуется невытесняющая многозадачность. Достоинство – изменяемая периодичность посылки сообщений. Недостаток – природа синхронных сообщений не гарантирует четкое выполнение периода прихода сообщений от таймера.

Создание потоков. Данный способ подразумевает определение некоторой процедуры потока, которая запускается параллельно основному процессу приложения. Момент окончания выполнения потока контролирует сама поточная процедура.

CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)Thread1,&params,0,&iThread);

Первый параметр определяет атрибуты безопасности для запускаемого потока. Если он равен NULL, то данный поток не может использоваться дочерними процессами.

Второй параметр определяет начальный адрес потока (фактически - имя процедуры потока), например:

DWORD WINAPI Thread1(PVOID pvoid)
{
// Текст потока
}

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

Четвертый параметр определяет дополнительные флаги создания потока. Если этот параметр равен нулю, то поток создастся немедленно.

Последний параметр является адресом переменной, в которую возвращается идентификатор потока.

Использование функции Sleep

Итак, поток – это часть программы, запускаемая параллельно другим задачам процесса. Поток сам определяет как долго ему находится в памяти и какие действия надо совершать. Если поток должен периодически совершать одни и те же действия (например, обновлять экран или проверять почту), то самым естественным способом сделать это является организация бесконечного цикла (подобного циклу обработки сообщений). Однако, в этом случае не определено время цикличности, т.к. временные периоды выполнения потока определены операционной системой. Кроме того, как только одна итерация цикла заканчивается, начинается другая. Как же вызвать приостановку выполнения потока? Для этих целей существует функция Sleep, которая в качестве единственного параметра имеет время, задаваемое в миллисекундах. Функция не осуществляет возврата до тех пор, пока не истечет указанное время. При этом другие потоки и процессы работают в обычных режимах. Если параметр этой функции равен нулю, то операционная система просто лишает текущий поток оставшегося кванта времени.

Следует отметить, что данная функция не освобождает полностью процессор от исполнения потока. Действительно, процессор периодически должен проверять истекло ли время, заданное в функции. То есть, речь идет не о приостановке периодического выполнения потока, а лишь о задержке выполнения алгоритма потока на одной его команде (Sleep).[4]

Критические разделы

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

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

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

Данный участок потока называется критическим разделом. Существует четыре функции работы с критическим разделом. Чтобы их использовать необходимо определеить объект типа критический раздел:

CRITICAL_SECTION cs;

Объект типа критический раздел сначала должен быть инициализирован одним из потоков программы с помощью функции:

InitializeCriticalSection(&cs);

Эта функция создает критический раздел с именем cs.После инициализации объекта "критический раздел" поток входит в критический раздел, вызывая функцию:

EnterCriticalSection(&cs);

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

LeaveCriticalSection(&cs);

В этот момент второй поток, задержанный в функции EnterCriticalSection(&cs), станет владельцем критического раздела, и его выполнение будет возобновлено.

Когда критический раздел больше не нужен вашей программе, его можно удалить с помощью функции:

DeleteCriticalSection(&cs);

Это приведет к освобождению всех ресурсов системы, задействованных для поддержки объекта критический раздел.

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

Использование событий

Альтернативным вариантом синхронизации является использование событий. Объект событие может быть либо свободным (signaled) или установленным (set), либо занятым (non-signaled) или сброшенным (reset). Вы можете создать объект "событие" с помощью функции:

hEvent = CreateEvent(&sa, fManual, fInitial, pszName);

Первый параметр - указатель на структуру SECURITY_ATTRIBUTES, устанавливающую параметры использования события потоками и процессами.

Параметр fInitial устанавливается в TRUE, если вы хотите, чтобы объект Событие был изначально свободным, или в FALSE, чтобы он был занятым.

Параметр psz Name определяет имя события. По этому имени разные процессы могут использовать одно событие.

Для того, чтобы сделать свободным существующий объект Событие, надо вызвать функцию:

SetEvent(hEvent);

Чтобы сделать объект Событие занятым, вызывается функция:

ResetEvent(hEvent);

Для синхронизации используется функция:

WaitForSingleObject(hEvent, dwTimeOut);

где второй параметр имеет значение INFINITE. Возврат из функции происходит немедленно, если объект событие в настоящее время свободен. В противном случае поток будет приостановлен в функции до тех пор, пока событие не станет свободным. Вы можете установить значение тайм-аута во втором параметре, задав его величину в миллисекундах. Тогда возврат из функции произойдет, когда объект Событие станет свободным или истечет тайм-аут.

Если параметр fManual имеет значение FALSE при вызове функции CreateEvent, то объект Событие автоматически становится занятым, когда осуществляется возврат из функции WaitForSingleObject. Эта особенность позволяет избежать использования функции ResetEvent.

Проблемные ситуации в многозадачных системах

Голодание (starvation)

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

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

Голодание создаёт узкое место в системе и не дает выжать из неё максимальную производительность, ограничиваемую только аппаратно обусловленными узкими местами.

Любое голодание вне 100 % загрузки процессора может быть устранено повышением приоритета голодающей нити, возможно — временным.

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

Гонка (race condition)

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

Устраняется добавлением необходимых блокировок и примитивов синхронизации. Обычно является легко устраняемым дефектом (забытая блокировка).

Инверсия приоритета

Поток L имеет низкий приоритет, поток M — средний, поток H — высокий. Поток L захватывает mutex, и, выполняясь с удержанием mutex’а, прерывается потоком M, который пробудился по какой-то причине, и имеет более высокий приоритет. Поток H пытается захватить mutex.

В полученной ситуации поток H ожидает завершения текущей работы потоком M, ибо, пока поток M исполняется, низкоприоритетный поток L не получает управления и не может освободить mutex.

Устраняется повышением приоритета всех нитей, захватывающих данный mutex, до одного и того же высокого значения на период удержания mutexa. Некоторые реализации mutex’ов делают это автоматически.

Процессы в Windows и потоковая многозадачность

Windows - это многозадачная операционная система, то есть она может одновременно выполнять две и более программ. Конечно, программы используют единственный процессор и, строго говоря, выполняются не одновремен­но. Однако высокое быстродействие компьютера создает такую иллюзию. Windows поддерживает два типа многозадачности: процессную и потоковую.

Взаимосвязь потоков и процессов

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

Windows - это не только многозадачная, но и многопоточная операционная система. Это означает, что в действительности про­граммы состоят из ряда более простых потоков выполнения. Выполнение этих потоков планируется так же, как и выполнение более мощных процессов.[5]

Примечания

  1. Википедия[Электронный ресурс]:Многозадачность./Дата обращения:07.03.2017. Режим доступа: [[1]]
  2. Инженерная группа 6-LAB[Электронный ресурс]: ИМС Propeller от PARALLAX ./Дата обращения:07.03.2017. Режим доступа: [[2]]
  3. Programming land [Электронный ресурс]: Многозадачные операционные системы — Дата обращения: 23.12.2016/ Режим доступа: http://programming-lang.com/html/visual_studio.net/glava12/index5.htm.
  4. StudFiles [Электронный ресурс]: Многозадачность и многопоточность — Дата обращения: 23.12.2016/ Режим доступа: http://www.studfiles.ru/preview/3577932/.
  5. Scritube[Электронный ресурс]:Процессы в Windows и потоковая многозадачность./Дата обращения:07.03.2017.Режим доступа:[[3]]

Источники