Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
СПро - все лк.pdf
Скачиваний:
65
Добавлен:
16.03.2016
Размер:
1.35 Mб
Скачать

Лекция №10: Многозадачность и многопоточность

Общие сведения

Многозадачность (multitasking) – это способность операционной системы выполнять несколько программ одновременно. В основе реализации этого принципа на персональных ЭВМ лежит использование операционной системой аппаратного таймера для выделения отрезков времени (time sliced) для каждого из одновременно выполняемых процессов. Если эти отрезки времени достаточно малы, и машина не перегружена слишком большим числом программ, то пользователю кажется, что все эти программы выполняются параллельно.

Многопоточность (multithreading) – режим, при котором машинная программа содержит в себе параллельно исполняемые участки или процедуры (threads). Таким образом, механизм многопоточности дает возможность программе самой быть многозадачной.

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

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

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

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

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

73

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

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

-работа основной программы пользователя;

-возникновение прерывания;

-сохранение параметров работающей программы (регистров процессора);

-переход по адресу процедуры обработки прерывания;

-выполнение процедуры обработки прерывания;

-восстановление параметров работающей программы;

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

прервать обработку текущего прерывания и т.д.

Прерывания использовались для написания так называемых резидентных

(terminate-and-stay-resident, TSR) программ. Данные программы позволяли писать драйверы клавиатуры, спулеры печати, копировальщики экрана, которые работали в фоновом режиме. Но порядок работы процессора оставался прежним – в конкретный момент времени процессор выполнял только одну программу, и только эта программа могла вернуть управление той, которую прервала. И, если происходил сбой в работе резидентной программы или при обработке прерывания, компьютер "зависал" и не давал возможности вернутся к прерванной программе.

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

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

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

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

74

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

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

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

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

Многопоточность

В многопоточной среде программы могут быть разделены на части, называемыми потоками выполнения (threads), которые выполняются одновременно. В терминах программы "поток" – это прости функция, которая может также вызывать другие функции программы. Программа начинает выполнятся со своего главного (первичного) потока, который в традиционных программах на языке С является функцией main, а в Windows-программах – WinMain. Будучи выполняемой, функция может создавать новые потоки обработки, выполняя системный вызов с указанием функции инициализации потока (initial threading function). Операционная система в вытесняющем режиме переключает управление между потоками подобно тому, как она это делает с процессами. Таким образом, многопоточность есть реализация принципа многозадачности внутри программы.

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

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

75

этой дополнительной информации хранятся, например, границы выделенной приложению памяти, что помогает аппаратно отслеживать корректности обращения к оперативной памяти со стороны приложения. Так как 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 при загрузке системы.

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

76

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

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

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

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

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

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

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

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

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

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

CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)Thread1,&params,0,&iThre

ad);

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

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

77