Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Создание эффективных приложений для Windows Джеффри Рихтер 2004 (Книга).pdf
Скачиваний:
385
Добавлен:
15.06.2014
Размер:
8.44 Mб
Скачать

интерфейсу Обратите внимание помечая флажок PreserveJob Time When Applymg Limits, Вы не устанавливаете ограничение, а просто получаете возможность изменять ограничения, не сбрасывая значения элементов ThisPeriod-

TotalUserTime и ThisPeriodTotalKemelTime при запросе базовой учетной информации.

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

Остальные кнопки позволяют управлять заданием по-другому. Кнопка Terminate Processes уничтожает все процессы в задании. Кнопка Spawn CMD In Job запускает командный процессор, сопоставляемый с заданием Из этого процесса можно запус кать дочерние процессы и наблюдать, как они ведут себя, став частью задания И пос ледняя кнопка, Put PID In Job, позволяет связать существующий свободный процесс с заданием (т. e. включить его в задание).

Список в нижней части окна отображает обновляемую каждые 10 секунд инфор мацию о статусе задания, базовые и расширенные сведения, статистику ввода-выво да, а также пиковые объемы памяти, занимаемые процессом и заданием.

Кроме этой информации, в списке показываются уведомления, поступающие от задания в порт завершения ввода-вывода. (Кстати, вся информация обновляется и при приеме уведомления.)

И еще одно: если Вы измените исходный код и будете создавать безымянный объект ядра «задание», то сможете запускать несколько копий этой программы, со здавая тем самым два и более объектов-заданий на одной машине. Это расширит Ваши возможности в экспериментах с заданиями.

Что касается исходного кода, то специально обсуждать его нет смысла — в нем и так достаточно комментариев. Замечу лишь, что в файле Job.h я определил С++-класс CJob, инкапсулирующий объект "задание» операционной системы. Эти избавило меня от необходимости передавать туда-сюда описатель задания и позволило уменьшить число операций приведения типов, которые обычно приходится выполнять при вы зове функций

QuerylnformationJobObject и SetInformationJobObject.

ГЛАВА 6 Базовые сведения о потоках

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

В главе 4 я говорил, что процесс фактически состоит из двух компонентов объекта ядра "процесс" и адресного пространства так вот, любой поток тожс состоит из двух компонентов.

объекта ядра, через который операционная система управляет потоком Там же хранится статистическая информация о потоке; стека потока, который содержит параметры всех функций и локальные пере

менные, необходимые потоку для выполнения кода (О том, как система управ ляет стеком потока, я расскажу в главе 16)

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

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

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

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

В каких случаях потоки создаются

Поток (thread) определяет последовательность исполнения кода в процессе. При инициализации процесса система всегда создает первичный поток Начинаясь со стартовою кодц из библиотеки С/С++, который в свою очередь вызывает входную функцию (WinMain, wWinMain, main или wmain) из Вашей программы, он живет до того момента, когда входная функция возвращает управление стартовому коду и тот вызывает функцию ExitProcess. Большинство приложений обходится единственным, первичным потоком. Однако процессы могут создавать дополнительные потоки, что позволяет им эффективнее выполнять свою работу

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

Вы активизируете службу индексации данных (content indexing service) Win dows 2000. Она создает поток с низким приоритетом, который, периодически пробуждаясь, индексирует содержимое файлов на дисковых устройствах Ва шего

компьютера. Чтобы найти какой-либо файл, Вы открываете окно Search Results (щелкнув кнопку Start и выбрав из меню Search команду For Files Or Folders) и вводите в поле Containing Text нужные критерии поиска. После это го начинается поиск по индексу, и на экране появляется список файлов, удов летворяющих этим критериям. Служба индексации данных значительно уве личивает скорость поиска, так как при ее использовании больше не требуется открывать, сканировать и закрывать каждый файл на диске

Вы запускаете программу для дефрагмептации дисков, поставляемую с Win dows 2000. Обычно утилиты такого рода предлагают массу настроек для адми нистрирования, в которых средний пользователь совершенно не разбирает ся, — например, когда и как часто проводить дефрагментацию Благодаря по токам с более низким приоритетом Вы можете пользоваться этой программой в фоновом режиме и дефрагментировать диски в те моменты, когда других дел у системы нет. Нетрудно представить будущую версию компилятора, способную автоматичес ки компилировать файлы исходного кода в паузах, возникающих при наборе текста программы. Тогда предупреждения и сообщения об ошибках появлялись бы практически в режиме реального времени, и Вы тут же видели бы, в чем Вы ошиблись Самое интересное, что Microsoft Visual Studio в какой-то мере уже умеет это делать, — обратите внимание на секцию ClassView в Workspace

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

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

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

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

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

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

документ обрабатывается на другом. Иначе говоря, какого-либо падения про изводительности не наблюдается. И кроме того, если компилятор из-за той или иной ошибки входит в бесконечный цикл, на остальных процессах это никак не отражает ся. (Конечно, о программах для MS-DOS и 16-разрядной Windows речь не идет,)

И в каких случаях потоки не создаются

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

Потоки - вещь невероятно полезная, когда ими пользуются с умом. Увы, решая старые проблемы, можно создать себе новые. Допустим, Вы разрабатываете тексто вый процессор и хотите выделить функциональный блок, отвечающий за распечат ку, в отдельный поток. Идея вроде неплоха: пользователь, отправив документ на рас печатку, может сразу вернуться к редактированию Но задумайтесь вот над чем. зна чит, информация в документе может быть изменена при распечатке документа? Как видите, теперь перед Вами совершенно новая проблема, с которой прежде сталкивать ся не приходилось. Тут-то и подумаешь, а стоит ли выделять печать в огдельный по ток, зачем искать лишних приключений? Но давайте разрешим при распечатке редак тирование любых документов, кроме того, который печатается в данный момент. Или так. скопируем документ во временный файл и отправим па печать именно его, а пользователь пусть редактирует оригинал в свое удовольствие. Когда распечатка вре менного файла закончится, мы его удалим — вот и все.

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

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

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

Несколько потоков пользовательского интерфейса в одном процессе можно об наружить в таких приложениях, как Windows Explorcr Он создаст отдельный поток для каждого окна папки. Это позволяет копировать файлы из одной папки в другую и попутно просматривать содержимое еще какой-то папки. Кроме того, если какая-то ошибка в Explorer приводит к краху одного из cro потоков, прочие потоки остаются работоспособны, и Вы можете пользоваться соответствующими окнами, пока не сде лаете что-нибудь такое, из-за чего рухнут и они. (Подробнее о потоках и пользова тельском интерфейсе см. главы 26 и 27.)

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