Стек пользовательского режима
(user-mode stack). Применяется для хранения
передаваемых в методы локальных переменных и аргументов. Также он содержит
адрес, показывающий, откуда начнет исполнение поток после того, как текущий
метод возвратит управление. По умолчанию на каждый стек пользовательского
режима Windows выделяет 1 Мбайт памяти (а точнее, резервирует 1 Мбайт па-
мяти и добавляет физическую память по мере необходимости при росте стека).
Стек режима ядра
(kernel-mode stack). Используется, когда код приложения
передает аргументы в функцию операционной системы, находящуюся в режиме
ядра. Для безопасности Windows копирует все аргументы, передаваемые в ядро
кодом в пользовательском режиме, из стека потока пользовательского режима
в стек режима ядра. После копирования ядро проверяет значения аргументов.
Так как код приложения не имеет доступа к стеку режима ядра, приложение не
в состоянии изменить уже проверенные аргументы, и с ними начинает рабо-
тать код ядра операционной системы. Кроме того, ядро вызывает собственные
методы и использует стек режима ядра для передачи локальных аргументов,
а также для сохранения локальных переменных функции и обратного адреса.
В 32-разрядной версии Windows стек режима ядра занимает 12 Кбайт, а в 64-раз-
рядной — 24 Кбайт.
Уведомления о создании и завершении
потоков.
Политика Windows такова,
что если в процессе создается поток, то для всех загруженных в этот процесс
DLL-библиотек вызывается метод
DllMain
и в него передается флаг
DLL_THREAD_
ATTACH
. Соответственно, при завершении потока этому методу передается уже
флаг
DLL_THREAD_DETACH
. Получая уведомления об этих событиях, некоторые
DLL-библиотеки выполняют специальные операции инициализации или
очистки для каждого созданного/завершенного в процессе потока. К примеру,
DLL-библиотека C-Runtime выделяет место под хранилище локальных состоя-
727
Ресурсоемкость.потоков
ний потока, необходимое для использования потоком функций из указанной
библиотеки.
На заре развития Windows в процесс загружалось 5 или 6 библиотек, в то время
как в наши дни некоторые процессы включают в себя несколько сотен библиотек.
Скажем, в адресное пространство приложения Microsoft Visual Studio на моем
компьютере загружено около 470 DLL-библиотек! Это означает, что созданный
в данном приложении новый поток получит возможность приступить к своей
работе только после вызова 470 функций из DLL. По завершении потока в Visual
Studio все эти функции будут вызваны снова. Разумеется, это не может не влиять
на производительность создания и завершения потоков в процессе
1
.
Теперь вы видите, каких затрат времени и памяти стоит создание потока, его
поддержание в системе и завершение. Но на самом деле ситуация еще хуже из-за
необходимости
переключения контекста
(context switching). Компьютер с одним
процессором может одновременно выполнять только что-то одно. Следовательно,
операционная система должна распределять физический процессор между всеми
своими потоками (логическими процессорами).
В произвольный момент времени Windows передает процессору на исполнение
один поток. Этот поток исполняется в течение некоторого временного интервала,
иногда называемого
тактом
(quantum). После завершения этого интервала кон-
текст Windows переключается на другой поток. При этом обязательно происходит
следующее:
1. Значения регистров процессора исполняющегося в данный момент потока со-
храняются в структуре контекста, которая располагается в ядре потока.
2. Из набора имеющихся потоков выделяется тот, которому будет передано управ-
ление. Если выбранный поток принадлежит другому процессу, Windows пере-
ключает для процессора виртуальное адресное пространство. Только после этого
возможно выполнение какого-либо кода или доступ к каким-либо данным.
3. Значения из выбранной структуры контекста потока загружаются в регистры
процессора.
После переключения контекста процессор исполняет выбранный поток, пока
не истечет выделенное потоку время, после этого снова происходит переключение
контекста. Windows делает это примерно каждые 30 мс. Никакого выигрыша в про-
изводительности или потреблении памяти переключение контекстов не дает. Оно
1
Библиотеки для C# и большинства других управляемых языков программирования не
имеют метода DllMain, поэтому управляемые DLL-библиотеки не получают уведомлений
DLL_THREAD_ATTACH и DLL_THREAD_DETACH. Что же касается неуправляемых
библиотек, то при помощи Win32-функции DisableThreadLibraryCalls они могут отключать
режим получения уведомлений. К сожалению, многие разработчики неуправляемого кода
не используют эту функцию просто потому, что не знают о ней.
728
Do'stlaringiz bilan baham: |