Реферат: Теория многозадачности и многопоточности

Введение

К моменту появленияперсональных компьютеров в мире существовало несколько технических решенийпозволяющих реализовать многозадачность на больших машинах. В бывшем СССР этобыли машины серии ЕС и болгарские ИЗОТ. Они теоретически позволяли подключатьдо 255 терминалов, где каждому терминалу выделялось некоторое количество ресурсовкомпьютера и процессорного времени. На практике нормальная работа такогокомплекса обеспечивалась при наличии не более 25-30 терминалов, или меньше присложных задачах.

Для персональных ЭВМмногозадачность не вводилась принципиально. Ведь исходя из названия PC – “Personal Computer” предполагалось, что работать будет одинчеловек с одной текущей задачей. В качестве операционной системы была принятапереработанная система CP/M под названием MS-DOS. Она так же не предусматривала многозадачности. Основнаяпроблема разработки многозадачной операционной системы это не реентерабильность ее функций. То есть если один процессзапустил функцию чтения файла, то другой процесс не сможет не только обращатьсяк файлам, но и вообще вызвать другие ее функции. Для этого необходима поддержкана уровне процессора которая была введена с разработкой линейки 286.

<span Times New Roman",«serif»;mso-fareast-font-family:«Times New Roman»; mso-ansi-language:RU;mso-fareast-language:RU;mso-bidi-language:AR-SA; layout-grid-mode:line">

 TOCo «1-3» Многозадачностьи многопоточность… PAGEREF_Toc468539865 h 1

Режимымногозадачности… PAGEREF_Toc468539866 h 2

Многозадачностьв DOS… PAGEREF_Toc468539867 h 2

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

PresentationManager и последовательная очередь сообщений… PAGEREF_Toc468539869 h 6

Решения,использующие многопоточность… PAGEREF_Toc468539870 h 7

Многопоточнаяархитектура… PAGEREF_Toc468539871 h 9

Коллизии,возникающие при использовании потоков… PAGEREF_Toc468539872 h 10

ПреимуществаWindows… PAGEREF_Toc468539873 h 11

Новаяусовершенствованная многопоточная программа… PAGEREF_Toc468539874 h 13

Оиспользовании функции Sleep… PAGEREF_Toc468539875 h 13

Критическийраздел… PAGEREF_Toc468539876 h 14

Объект Mutex… PAGEREF_Toc468539877 h 17

Уведомления особытиях… PAGEREF_Toc468539878 h 18

Локальнаяпамять потока… PAGEREF_Toc468539879 h 18

<span Arial",«sans-serif»;mso-fareast-font-family: «Times New Roman»;mso-bidi-font-family:«Times New Roman»;mso-font-kerning:14.0pt; mso-ansi-language:RU;mso-fareast-language:RU;mso-bidi-language:AR-SA">
Многозадачностьи многопоточность

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

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

Для того, чтобы многозадачность стала реальностью на персональныхкомпьютерах, потребовалось достаточно много времени. Но, кажется, сейчас мыприближаемся к эпохе использования многозадачности на ПК (PC). Как мы увидим вскоре, некоторыерасширенные 16-разрядные версииWindows поддерживают многозадачность, а имеющиеся теперь в нашемраспоряженииWindows NTиWindows95 – 32-разрядные версии Windows, поддерживают кромемногозадачности еще и многопоточность (multithreading).

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

Режимы многозадачности

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

Многозадачность вDOS

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

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

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

Некоторые производители программного обеспечения пытались создатьмногозадачные оболочки или оболочки, использующие переключение между задачами,как надстройки надDOS(например,Quarterdeck's DeskView), но только одна из этих оболочек получилаширокое распространение на рынке. Это, конечно.Windows.

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

КогдаMicrosoftвыпустила на рынокWindows1.0 в1985году, это было еще в большой степени искусственным решением, придуманным дляпреодоления ограниченийMSDOS. В то времяWindowsработала в реальном режиме(real mode), но даже тогда она была способна перемещать блоки физическойпамяти (одно из необходимых условий многозадачности) и делала это, хотя и неочень прозрачно для приложений, но все-таки вполне удовлетворительно.

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

Оконная оболочка позволяет нескольким программам выполнятьсясовместно, разделяя один экран. Переключение вперед и назад становитсятривиальным, существует возможность быстро передавать данные из одной программыв другую, например, разместить картинку, созданную в программе рисования, втекстовом файле, образованном с помощью текстового процессора. Передача данныхподдерживалась в различных версияхWindows: сначала с использованием папки обмена(clipboard), позднее– посредством механизма динамического обменаданными(Dynamic DataExchange, DDE), сейчас– через внедрение и связывание объектов(Object Linking and Embedding,OLE).

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

16-разрядные версииWindows не имели возможности произвольно переключать управление с однойWindows-программы на другую, основываясь наквантах времени таймера. Переключение между задачами происходило в момент,когда программа завершала обработку сообщения и возвращала управлениеWindows. Такую невытесняющую многозадачность называют также кооперативноймногозадачностью(cooperativemultitasking) потому, что она требует некоторого согласования междуприложениями. ОднаWindows-программамогла парализовать работу всей системы, если ей требовалось много времени дляобработки сообщения.

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

Windowsиспользовала вытесняющую многозадачность для выполнения DOS-программ, а также позволяла библиотекам динамической компоновки(DLL) получать прерыванияаппаратного таймера для задач мультимедиа.

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

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

Presentation Manager и последовательная очередь сообщений

Первой попыткой фирмыMicrosoft (в сотрудничестве сIBM) внедрить многозадачность вквази-DOS/Windows оболочку была системаOS/2 и PresentationManager (PM). ХотяOS/2, конечно,поддерживала вытесняющую многозадачность, часто казалось, что это вытеснение небыло перенесено вPM. Дело в том, чтоPM выстраивал в очередь сообщения, формируемыев результате пользовательского ввода от клавиатуры или мыши. Это означает, чтоPM не предоставляет программе такоепользовательское сообщение до тех пор, пока предыдущее сообщение, введенноепользователем, не будет полностью обработано.

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

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

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

Решения, использующие многопоточность

Выше был рассмотренPresentation Manager операционной системыOS/2 только из-за того, что это была первая оболочка, котораяподготовила сознание некоторых ветеранов программирования подWindows (в том числе и автора) квведению многопоточности. Интересно, что ограниченная поддержка многопоточности в РМ дала программистам основную идеюорганизации программ, использующих многопоточность.Хотя эти ограничения сейчас преимущественно преодолены вWindows95, тем не менее уроки, полученные при работе с более ограниченнымисистемами, остаются актуальными и по сей день.

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

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

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

Таким образом, программисты, работавшие с РМ, научились разбиватьсвои программы на один поток с очередью сообщений, создающий все окна иобрабатывающий сообщения для них, и один или несколько потоков, не имеющихочередей сообщений, и выполняющих продолжительные действия в фоновом режиме.Кроме того, программисты, работавшие с РМ, узнали о «правиле1/10 секунды». Оно состоит в том, чтопоток с очередью сообщений тратит не более1/10секунды на обработку любого сообщения. Все, что требует большего времени,следовало выделять в отдельный поток. Если все программисты придерживалисьэтого правила, то никакая РМ-программа не моглавызвать зависание системы более чем на1/10секунды.

Многопоточная архитектура

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

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

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

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

Коллизии, возникающие при использованиипотоков

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

Одной из основных ошибок в многопоточных программах является такназываемое состояние гонки(race condition). Это случается, если программист считает, что один потокзакончит выполнение своих действий, например, подготовку каких-либо данных, дотого, как эти данные потребуются другому потоку. Для координации действийпотоков операционным системам необходимы различные формы синхронизации. Однойиз таких форм является семафор(semaphore), который позволяет программисту приостановить выполнениепотока в конкретной точке программы до тех пор, пока он не получит от другогопотока сигнал о том, что он может возобновить работу. Похожи на семафорыкритические разделы(criticalsections), которые представляют собой разделы кода, во время выполнениякоторого, поток не может быть прерван.

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

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

lCount++;

гдеICount–32-разрядная глобальная переменная типа длинное целое, используемая другимипотоками. В 16-разрядной программе, в которой такой оператор языка Странслируется в две инструкции машинного кода (сначала инкрементируется младшие16 разрядов, а затем добавляется перенос встаршие16 разрядов). Допустим, чтооперационная система прервала поток между этими двумя инструкциями машинногокода. Если переменнаяICount имела значение$0000FFFF, то после выполнения первой инструкции машинного кодаICountбудет иметь нулевое значение. Если в этот момент произойдет прерывание потока,то другой поток получит нулевое значение переменной ICount. Только послеокончания этого потока значениеICount будет увеличено на единицудо своего истинного значения$00010000.

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

ПреимуществаWindows

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

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

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

ВWindows95 иWindows NT есть функция, которая позволяет одному потокууничтожить другой поток, принадлежащий тому же процессу. Как вы обнаружите,когда начнете писать многопоточные приложения подWindows, иногда это очень удобно.Ранние версии операционной системыOS/2не содержали функции для уничтожения потоков.

Windows95 иWindows NT поддерживают так называемую локальную память потока(thread local storage, TLS).Для того чтобы понять, что это такое, вспомним о том, что статическиепеременные, как глобальные так и локальные по отношению к функциям, разделяютсямежду потоками, поскольку они расположены в зоне памяти данных процесса.Автоматические переменные (которые являются всегда локальными по отношению кфункции)– уникальны для каждого потока,т. к. они располагаются в стеке, а каждый поток имеет свой стек.

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

Новая усовершенствованная многопоточнаяпрограмма

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

Есть даже некоторые ситуации, когда появление курсора мыши в видепесочных часов, может быть совершенно подходящим. Выше уже упоминалось«правило1/10 секунды». Таквот, загрузка большого файла в память может потребовать больше времени, чем1/10 секунды. Значит ли это, что функциизагрузки файла должны были быть реализованы с использованием разделения напотоки? Совсем необязательно. Когда пользователь дает программе команду открытьфайл, то он или она обычно хочет, чтобы операционная система выполнила еенемедленно. Выделение процесса загрузки файла в отдельный поток просто приведетк усложнению программы. Не стоит это делать даже ради того, чтобы похвастатьсяперед друзьями, что вы пишите многопоточные приложения.

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

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

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

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

Если параметр функцииSleepзадан равным нулю, то поток будет лишен остатка выделенного ему квантапроцессорного времени.

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

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

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

Даже в многозадачной операционной системе большинство программвыполняются независимо друг от друга. Но некоторые проблемы все же могутвозникнуть. Например, двум программам может понадобиться читать и писать в одинфайл в одно и то же время. Для таких случаев операционная система поддерживаетмеханизм разделения файлов(shared files) и блокирования отдельных фрагментов файла(record locking).

Однако, в операционной системе, поддерживающей многопоточность,такое решение может внести путаницу и создать потенциальную опасность.Разделение

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

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

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

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

CRITICAL_SECTION CS ;

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

InitializeCriticalSection(&cs);

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

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

EnterCriticalSection(&cs) ;

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

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

LeaveCriticalSection(&cs);

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

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

DeleteCriticalSection(&cs);

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

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

Обратите внимание, что возможно определение нескольких объектовтипа критический раздел, например,cs1</spa

еще рефераты
Еще работы по программированию, базе данных