Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
У. Столлингс ГЛАВА 4 Потоки.doc
Скачиваний:
49
Добавлен:
11.05.2015
Размер:
6.22 Mб
Скачать

Функциональность потоков

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

Состояния потоков

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

С изменением состояния потоков связаны такие четыре основных действия [ANDE97].

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

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

  • Разблокирование. Когда наступает событие, ожидание которого блокирова­ло поток, последний переходит в состояние готовности.

  • Завершение. После завершения потока его контекст регистров и стеки уда­ляются.

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

Мы еще вернемся к обсуждению этого вопроса при сравнении потоков на пользовательском уровне и потоков на уровне ядра, а пока что рассмотрим выигрыш в производительности при использовании потоков, которые не бло­кируют весь процесс. На рис. 4.3 (из [KLEI96]) показана программа, выпол­няющая два вызова удаленных процедур (remote procedure call — RPC)2 на двух разных узлах, чтобы получить результат после их совместного выпол­нения. В однопоточной программе результаты получаются последовательно, поэтому программа должна ожидать, пока от каждого сервера по очереди бу­дет получен ответ. Переписав программу так, чтобы для каждого вызова удаленной процедуры она использовала отдельный поток, можно получить существенный выигрыш в скорости. Заметим, что если такая программа ра­ботает на однопроцессорной машине, то запросы будут генерироваться после­довательно; результаты тоже будут получены последовательно, однако про­грамма будет ожидать двух ответов одновременно.

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

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

Синхронизация потоков

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

При рассмотрении синхронизации потоков возникают те же вопросы и ис­пользуются те же методы, что и при синхронизации процессов. Эти вопросы и методы обсуждаются в главе 5, "Параллельные вычисления: взаимоисключения и многозадачность", и главе 6, "Взаимоблокировка и голодание".

Пример: Adobe PageMaker

Рассмотрим использование потоков на примере приложения Adobe Page­Maker, работающего под управлением операционной системы Linux. Программа PageMaker является настольным издательским средством, предназначенным для создания и форматирования документов. Для оптимизации скорости отклика этого приложения была выбрана потоковая структура, показанная на рис. 4.5 [KRON90]. Три потока активны всегда — поток, отвечающий за обработку собы­тий, поток, обновляющий экран, и служебный поток.

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

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

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

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

  1. В программе PageMaker количество объектов на странице не ограничено, и поэтому обработка запроса на обновление экрана может оказаться весьма длительной.

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

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

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