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

#else

#error Module contains CPU-specific code, modify and recompile. #endif

//вносим изменения в регистры потока, ContextFlags

//можно и не инициализировать, так как это уже сделано

Context.ConlrolFlags = CONTEXT_CONTROL; SetThreadContext(hThread, &Context);

//возобновляем выполнение потока; оно начнется с адреса

0x00010000

ResumeThread(hThread);

Этот код, вероятно, приведет к ошибке защиты (нарушению доступа) в удаленном потоке; система сообщит о необработанном исключении, и удаленный процесс бу дет закрыт. Все верно — нс Ваш, а удаленный. Вы благополучно обрушили другой процесс, оставив свой в целости и сохранности!

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

Подробнее о структуре CONTEXT мы поговорим в главе 24.

Приоритеты потоков

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

Каждому потоку присваивается уровень приоритета — от 0 (самый низкий) до 31 (самый высокий). Решая, какому потоку выделить процессорное премя, система сна чала рассматривает только потоки с приоритетом 31 и подключает их к процессору по принципу карусели. Если поток с приоритетом 31 нс исключен из планирования, он немедленно получает квант времени, по истечении которого система проверяет, есть ли еще один такой поток. Если да, он тоже получает свой квант процессорного времени.

Пока в системе имеются планируемые потоки с приоритетом 31, ни один поток с более низким приоритетом процессорного времени не получает. Такая ситуация на зывается "голоданием* (starvation). Она наблюдается, когда потоки с более высоким приоритетом так интенсивно используют процессорное время, что остальные прак тически не работают. Вероятность этой ситуации намного ниже в многопроцессор ных системах, где потоки с приоритетами 31 и 30 могут выполняться одновременно. Система всегда старается, чтобы процессоры были загружены работой, и они проста ивают только в отсутствие планируемых потоков.

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

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

А теперь обратите внимание на еще один момент. Потоки с более высоким при оритетом всегда вытесняют потоки с более низким приоритетом независимо от того, исполняются последние или нет. Допустим, процессор исполняет поток с приорите том 5, и тут система обнаруживает, что поток с более высоким приоритетом готов к выполнению. Что будет? Система остановит поток с более низким приоритетом — даже ссли не истек отведенный ему квант процессорного времени — и подключит к процессору поток с более высоким приоритетом (и, между прочим, выдаст ему пол ный квант времени),

Кстати, при загрузке системы создается особый поток — поток обнуления стра ниц (zero page thread), которому присваивается нулевой уровень приоритета. Ни один поток, кроме этого, не может иметь нулевой уровень приоритета Он обнуляет сво бодные страницы в оперативной памяти при отсутствии других потоков, требующих внимания со стороны системы.

Абстрагирование приоритетов

Создавая планировщик потоков, разработчики из Microsoft прекрасно понимали, что он не подойдет на все случаи жизни. Они также осознавали, что со временем "назна чение" компьютера может измениться Например, в момент выпуска Windows NT со здание приложений с поддержкой OLE еще только начиналось. Теперь такие прило жения — обычное дело. Кроме того, значительно расширилось применение игрово го программного обеспечения, ну и, конечно же, Интернета

Алгоритм планирования потоков существенно влияет на выполнение приложений. С самого начала разработчики Microsott понимали, что его придется изменять по мере того, как будут расширяться сферы применения компьютеров Microsoft гарантирует, что наши программы будут работать и в следующих версиях Windows. Как же ей уда ется изменять внутреннее устройство системы, не нарушая работоспособность наших программ? Ответ в том, что:

планировщик документируется не полностью;

Microsoft не разрешает в полной мере использовать все особенности плани ровщика;

Microsoft предупреждает, что алгоритм работы планировщика постоянно ме няется, и не рекомендует писать программы в расчете на текущий алгоритм

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

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

Windows поддерживает шесть классов приоритета; idle (простаивающий), below normal (ниже обычного), normal (обычный), above normal (выше обычного), high (вы сокий) и realtime (реального времени). Самый распространенный класс приоритета, естественно, — normal; его использует 99% приложений. Классы приоритета показа ны в следующей таблице.

Класс приоритета

Real-lime

High

Above normal

Normal

Below normal

Idle

Описание

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

Потоки в этом процессе тоже должны немедленно реагировать на со бытия, обеспечивая выполнение критических по времени задач Этот класс присвоен, например, Task Manager, что дает возможность пользо вателю закрывать больше неконтролируемые процессы

Класс приоритета, промежуточный между normal и high. Это новый класс, введенный в Windows 2000.

Потоки в этом процессе не предъявляют особых требований к выделе нию им процессорного времени.

Класс приоритета, промежуточный между normal и idle. Это новый класс, введенный в Windows

2000.

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

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

Класс приоритета high следует использовать лишь при крайней необходимости Может, Вы этого и нс знаете, но Explorer выполняется с высоким приоритетом. Боль шую часть времени его потоки простаивают, готовые пробудиться, кактолько пользо ватель нажмет какую-нибудь клавишу или щелкнет кнопку мыши. Пока потоки Explorer простаивают, система не выделяет им процессорное время, что позволяет выполнять потоки с более низким приоритетом Но вот пользователь нажал, скажем, Ctrl+Esc, и система пробуждает поток Explorer. (Комбинация клавиш Ctrl+Esc попутно открыва ет меню Start.) Если в данный момент исполняются потоки с более низким приори тетом, они немедленно вытесняются, и начинает работать поток Explorer Microsoft разработала Explorer именно так потому, что любой пользователь — независимо от текущей ситуации в системе — ожидает мгновенной реакции оболочки на свои ко манды R сущности, окна Explorcr можно открывать, даже когда все потоки с более низким приоритетом зависают в бесконечных циклах Обладая более высоким при оритетом, потоки Explorer вытесняют поток, исполняющий бесконечный цикл, и дают возможность закрыть зависший процесс.

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

Классом приоритета real-time почти никогда не стоит пользоваться На самом деле в ранних бета-версиях Windows NT 3.1 присвоение этого класса приоритета прило жениям даже не предусматривалось, хотя операционная система поддерживала эту возможность. Real-time — чрезвычайно высокий приоритет, и, поскольку большин ство потоков в системе (включая управляющие самой системой) имеет более низкий приоритет, процесс с таким классом окажет на них сильное влияние. Так, потоки реального времени могут заблокировать необходимые операции дискового и сетевого ввода-вывода и привести к несвоевременной обработке ввода от мыши и клавиату ры — пользователь может подумать, что система зависла. У Вас должна быть очень веская причина для применения класса real-time — например, программе требуется

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

NOTE:

Процесс с классом приоритета real-time нельзя запустить, если пользователь не имеет привилегии Increase Scheduling Priority. По умолчанию такой привилеги ей обладает администратор и пользователь с расширенными полномочиями.

Конечно, большинство процессов имеет обычный класс приоритета. В Windows 2000 появилось два новых промежуточных класса — below normal и above normal Microsoft добавила их, поскольку некоторые компании жаловались, что существующий набор классов приоритетов не дает должной гибкости.

Выбрав класс приоритета, забудьте о том, как Ваша программа будет выполняться совместно с другими приложениями, и сосредоточьтесь на ее потоках. Windows под держивает семь относительных приоритетов потоков: idle (простаивающий), lowcst (низший), below normal (ниже обычного), normal (обычный), above normal (выше обычного), highest (высший) и time-critical (критичный по времени) Эти приорите ты относительны классу приоритета процесса Как обычно, большинство потоков использует обычный приоритет. Относительные приоритеты потоков описаны в сле дующей таблице.

Относительный

Описание

приоритет потока

 

Time-critical

Поток выполняется с приоритетом 31 в классе real-time и с

 

приоритетом 15 в других классах

 

 

Highest

Поток выполняется с приоритетом на два уровня выше

 

обычною для данного класса

 

 

Above normal

Поток выполняется с приоритетом на один уровень выше

 

обычного для данного класса

 

 

Normal

Поток выполняется с обычным приоритетом процесса для

 

данного класса

 

 

Below normal

Поток выполняется с приоритетом на один уровень ниже

 

обычного для данного класса

 

 

Lowest

Поток выполняется с приоритетом на два уровня ниже

 

обычного для данного класса

 

 

Idle

Поток выполняется с приоритетом 16 в классе real-time и с

 

приоритетом 1 в других классах

 

 

Итак, Вы присваиваете процессу некий класс приоритета и можете изменять от носительные приоритеты потоков в пределах процесса. Заметьте, что я не сказал ни слова об уровнях приоритетов 0-31. Разработчики приложений не имеют с ними дела. Уровень приоритета формируется самой системой, исходя из класса приоритета про цесса и относительного приоритета потока, А механизм его формирования — как раз то, чем Microsoft не хочет себя ограничивать И действительно, этот механизм меня ется практически в каждой версии системы.

В следующей таблице показано, как формируется уровень приоритета в Win dows 2000, но не забывайте, что в Windows NT и тем более в Windows 95/98 этот механизм действует несколько иначе Учтите также, что в будущих версиях Windows он вновь изменится.

Например, обычный поток в обычном процессе получает уровень приоритета 8, Поскольку большинство процессов имеет класс normal, a большинство потоков —

относительный приоритет normal, y основной части потоков в системе уровень при оритета равен 8.

Обычный поток в процессе с классом приоритета high получает уровень приори тета 13. Изменив класс приоритета процесса на idle, Вы снизите уровень приоритета того же потока до 4. Вспомните, что приоритет потока всегда относителен классу приоритета его процесса Изменение класса приоритета процесса не влияет на от носительные приоритеты его потоков, но сказывается на уровне их приоритета

Относительный приоритет потока

Idle

Класс приоритета процесса

Real-

 

 

 

 

 

 

time

 

 

Below

Normal

Above

High

 

 

 

normal

 

normal

 

 

 

 

 

 

 

 

 

Time-critical (критичный по времени)

15

15

15

15

15

31

 

 

 

 

 

 

 

Highest (высший)

6

8

10

12

15

26

 

 

 

 

 

 

 

Above normal (выше обычного)

5

7

9

11

14

25

 

 

 

 

 

 

 

Normal (обычный)

4

6

8

10

13

24

 

 

 

 

 

 

 

Below normal (ниже обычного)

3

5

7

9

12

23

 

 

 

 

 

 

 

Lowest (низший)

2

4

6

8

11

22

 

 

 

 

 

 

 

Idle (простаивающий)

1

1

1

1

1

16

 

 

 

 

 

 

 

Обратите внимание, что в таблице не показано, как задать уровень приоритета 0. Это связано с тем, что нулевой приоритет зарезервирован для потока обнуления стра ниц, и никакой другой поток не может иметь такой приоритет. Кроме того, уровни 17-21 и 27-30 в обычном приложении тоже недоступны. Вы можете пользоваться ими, только если пишете драйвер устройства, работающий в режиме ядра. И еще одно: уровень приоритета потока в процессе с классом real-time не может опускаться ниже 16, а потока в процессе с любым другим классом — подниматься выше 15.

NOTE

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