Контрольная работа: Многозадачность и многопоточность

Внимание! Если размещение файла нарушает Ваши авторские права, то обязательно сообщите нам

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

Имеется четыре функции для работы с критическими разделами. Чтобы их использовать, вам необходимо определить объект типа критический раздел, который является глобальной переменной типа CRITICAL_SECTION. Например,

CRITICAL_SECTION CS;

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

InitializeCriticalSection (&cs);

Эта функция создает объект критический раздел с именем cs. В документации содержится следующее предупреждение: «Объект критический раздел не может быть перемещен или скопирован. Процесс также не должен модифицировать объект, а должен обращаться с ним, как с «черным ящиком».»

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

EnterCriticalSection (&cs);

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

LeaveCriticalSection (&cs);

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

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

DeleteCriticalSection (&cs);

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

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

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

Обратите внимание, что надо быть весьма осторожным при использовании критического раздела в главном потоке. Если вторичный поток проводит слишком много времени в его собственном критическом разделе, то это может привести к «зависанию» главного потока на слишком большой период времени.

Объект Mutex

Существует одно ограничение в использовании критических разделов. Оно заключается в том, что их можно применять для синхронизации потоков только в рамках одного процесса. Но бывают случаи, когда необходимо синхронизировать действия потоков различных процессов, которые разделяют какие-либо ресурсы (например, память). Использовать критические разделы в такой ситуации нельзя. Вместо них подключаются объекты типа mutex (mutexobject).

Составное слово «mutex» происходит из словосочетания «mutualexclusion», что означает взаимное исключение, и очень точно отражает назначение объектов. Мы хотим предотвратить возможность прерывания потока в программе до тех пор, пока не будет выполнено обновление или использование разделяемых данных.

Уведомления о событиях

Мы можем определить понятие большой работы как действия, выполняя которые, программа нарушит «правило 1/10 секунды». Примерами большой работы могут служить: проверка орфографии в текстовых процессорах, сортировка и индексирование файлов баз данных, пересчет электронной таблицы, печать и даже сложное рисование. Конечно, как мы уже знаем, лучшее решение состоит в следовании «правилу 1/10 секунды», т.е. в передаче большой работы вторичным потокам обработки. Эти вторичные потоки не создают окон и, значит, не ограничены «правилом 1/10 секунды».

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

Локальная память потока

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

Может возникнуть необходимость иметь постоянную область памяти, уникальную для каждого потока. Например, функция strtok языка С, которая уже упоминалась в этой главе, требует такого типа память. Нет сомнений, что С его не поддерживает. В Windows 95 имеетсячетыре функции, поддерживающие эту память, которая называется локальной памятью потока (threadlocalstorage, TLS).

Первичный поток вызывает функцию JTsAlloc для получения значения индекса:

dwTlsIndex = TIsAlloc ();

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

Функция потока начинается с выделения памяти для структуры данных и с вызова функции TIsSetValue, используя индекс, полученный ранее:

TIsSetValue (dwTlsIndex, GlobalAlloc (GPTR, sizeof (DATA)));

Это действие устанавливает соответствие указателя с конкретным потоком и конкретным индексом в потоке. Теперь, любая функция, которой нужно использовать этот указатель (включая саму базовую функцию потока), может использовать код, подобный такому:

PDATA pdata;

pdata = (PDATA) TIsGetValue (dwTlsIndex);

Теперь она может изменять значения pdata->a и pdata->b. Перед завершением функции потока необходимо освободить захваченную память:

GlobalFree (TIsGetValue (dwTlsIndex));

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

TIsFree (dwTlsIndex);

Полезно посмотреть как организована локальная память потока. (Мне неизвестно, как в действительности Windows 95 это делает, но описываемая схема вполне правдоподобна.) Во-первых, функция TIsAlloc могла бы просто выделить блок памяти (длиной 0 байт) и вернуть значение индекса, который является указателем на этот блок. Каждый раз при вызове функции TIsSetValue с этим индексом блок памяти увеличивается на 8 байт с помощью функции GlobalReAlloc. В этих 8 байтах хранятся идентификатор потока, вызывающего функцию, полученный с помощью функции GetCurrentThreadID, и указатель, переданный функции TIsSetValue. Функция TIsGetValue просто использует идентификатор потока для поиска в таблице, а затем возвращает указатель. Функция TZsFree освобождает блок памяти.

Реализация многопоточности в Delphi

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

Базовый класс для создания потока пользователя - TThread

TThread = class

protected

procedureDoTerminate; virtual;

procedure Execute; virtual;

procedure Synchronize (Method: TThreadMethod);

propertyReturnValue: Integer;

property Terminated: Boolean;

public

constructor Create (CreateSuspended: Boolean);

procedure Resume;

procedure Suspend;

procedure Terminate;

functionWaitFor: LongWord;

propertyFreeOnTerminate: Boolean;

property Handle: Thandle;

property Priority: TthreadPriority;

property Suspended: Boolean;

propertyThreadID: Thandle;

propertyOnTerminate: TnotifyEvent;

end;

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

Для вызова методов VCL необходимо синхронизировать дочерний поток с главным. Для этого служит процедура Synchronize (Method: TThreadMethod);

unit Unit1;

interface

uses

Classes;

type

TSamples = class(TThread)

private

{Private declarations}

protected

procedure Execute; override;

end;

implementation

{Подсказка Delphi поповоду Synchronize.

Important: Methods and properties of objects in VCL can only be used in a

method called using Synchronize, for example,

Synchronize(UpdateCaption);

andUpdateCaption could look like,

procedureSamples. UpdateCaption;

begin

Form1. Caption:= 'Updated in a thread';

end;}

{Samples}

procedureTSamples. Execute;

begin

{Здесь должен быть размещен код потока}

end;

end.

многозадачность поток windows

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

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

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

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

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

Список используемой литературы

1. Turbo Pascal for Windows в 2-х томах. Нейл Рубенкинг Пер. с англ. - М.: Мир, 1993, 536 с., ил.

2. Теория и практика C++. Герберт Шилдт. пер. с англ. - СПб.: BHV - Санкт-Петербург, 1996. 416 с., ил.

3. Программирование для Windows 95; в 2-х томах. Чарльз Петзолд. пер. с англ. - СПб: BHV - Санкт-Петербург, 1997. - 752 с., ил.

4. Микропроцессоры 80x86 Архитектура, функционирование. В.М. Михальчук А.А. Ровдо С.В. Рыжиков Мн.: Битрикс, 1994. - 400 с.