Классическая модель потоков

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

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

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

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

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

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

Рис. 2.8. а — три процесса, у каждого из которых по одному потоку; б — один процесс с тремя потоками


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

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

Таблица 2.4. Использование объектов потоками
Элементы, присущие каждому процессу Элементы, присущие каждому потоку
Адресное пространство Счетчик команд
Глобальные переменные Регистры
Открытые файлы Стек
Дочерние процессы Состояние
Необработанные аварийные сигналы
Сигналы и обработчики сигналов
Учетная информация


Элементы в первом столбце относятся к свойствам процесса, а не потоков.

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

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

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

Рис. 2.9. У каждого потока имеется собственный стек


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

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

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

Хотя потоки зачастую приносят пользу, они вносят в модель программирования и ряд сложностей. Для начала рассмотрим эффект, возникающий при осуществлении системного вызова fork, принадлежащего ОС UNIX. Если у родительского процесса есть несколько потоков, должны ли они быть у дочернего процесса? Если нет, то процесс может неверно функционировать из-за того, что все они составляют его неотъемлемую часть.

Но если дочерний процесс получает столько же потоков, сколько их было у родительского процесса, что произойдет, если какой-нибудь из потоков родительского процесса был заблокирован системным вызовом read, используемым, к примеру, для чтения с клавиатуры? Будут ли теперь два потока, в родительском и в дочернем процессах, заблокированы на вводе с клавиатуры? Если будет набрана строка, получат ли оба потока ее копию? Или ее получит только поток родительского процесса? А может быть, она будет получена только потоком дочернего процесса? Сходные проблемы существуют и при открытых сетевых подключениях.

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

2.2.3.

<< | >>
Источник: Э. ТАНЕНБАУМ Х. БОС. СОВРЕМЕННЫЕ ОПЕРАЦИОННЫЕ СИСТЕМ Ы 4-е ИЗДАНИЕ. 2015

Еще по теме Классическая модель потоков:

  1. Классическая модель демократии
  2. коян: Восходящий узел - включение в общий поток; Нисходящий узел - исключение из общего потока.
  3. ПОТОК СОЗНАНИЯ
  4. Альбатрос (восхождение на поток)
  5. ТЕОРИЯ ПОТОКА СОЗНАНИЯ
  6. 3.9. ПОТОК СОЗНАНИЯ
  7. 2.2.1. Поток образов
  8. Глава 3. ОТКРОЙТЕ СВОЙ ПОТОК ОБРАЗОВ
  9. 12.2.1. Групповой поток образов
  10. МИХАЙ ЧИКСЕНТМИХАЙИ. В ПОИСКАХ ПОТОКА, 2015
  11. 13.5.1. Игра в поток образов
  12. 2.7. КАК ВЫЗВАТЬ ПОТОК ОБРАЗОВ