Переключение потоков
Планировщик изменяет класс процесса, связанного с этим окном, так чтобы он был больше или равен классу любого процесса, связанного с background-окном. Класс приоритета вновь восстанавливается при потере процессом статуса foreground. Отметьте, что в Windows NT/2000 пользователь может управлять величиной ускорения всех процессов класса NORMAL_PRIORITY с помощью панели управления (команда System, вкладка Performance, ползунок Boost Application Performance).
Когда окно получает сообщение типа WM_TIMER, WM_LBUTTONDOWN или WM_KEYDOWN, планировщик также ускоряет (boosts) ноток, владеющий этим окном. Существуют еще ситуации, когда планировщик временно повышает уровень приоритета потока. Довольно часто потоки ожидают возможности обратиться к диску. Когда диск освобождается, блокированный поток просыпается и в этот момент система повышает его уровень приоритета. После ускорения потока планировщик постепенно снижает уровень приоритета до базового значения. Уровень снижается на одну единицу после завершения очередного кванта времени. Иногда система инвертирует приоритеты, чтобы разрешить конфликты типа deadlock. Благодаря динамике изменения приоритетов потоки активного процесса вытесняют потоки фонового процесса, а потоки с низким приоритетом все-таки имеют шанс получить управление.
Представьте такую ситуацию: поток 1 с высоким приоритетом вынужден ждать, пока поток 2 с низким приоритетом выполняет код в критической секции. В это же время готов к выполнению поток 3 со средним значением приоритета. Он получает время процессора, а два других потока застревают на неопределенное время, так как поток 2 не в состоянии вытеснить поток 3, а поток 1 помнит, что надо ждать, когда поток 2 выйдет из критической секции. Отметьте, что планировщик не способен провести анализ подобной ситуации и решить проблему. Он видит ситуацию только в свете существования двух готовых (ready) потоков с разными приоритетами.
Windows NT/2000 разрешает эту ситуацию так. Планировщик увеличивает приоритеты готовых потоков на величину, выбранную случайным образом. В нашем примере это приводит к тому, что поток с низким приоритетом получает шанс на время процессора и, в течение, может быть, нескольких квантов закончит выполнение кодов критической секции. Как только это произойдет, поток 1 с высоким приоритетом сразу получит управление и сможет, вытеснив поток 3, начать выполнение кодов критической секции.
Windows 95 разрешит эту ситуацию по-другому. Она определяет факт того, что поток 1 с высоким приоритетом зависит от потока 2 с низким приоритетом, и повышает приоритет второго потока до величины приоритета первого. Это приводит к тому, что поток 3 вытесняется потоком 2 и он, закончив выполнение кодов критической секции, пропускает вперед ждавший поток 1.
В системе Windows NT/2000 программист имеет возможность управлять процессом ускорения потоков с помощью API-функций SetProcessPriorityBoost (все потоки данного процесса) или SetThreadPriorityBoost (данный поток) в пределах, которые обозначены на рис. 12.7. Функции GetProcessPriorityBoost и GetThreadPriorityBoost позволяют выяснить текущее состояние флага.
Рис. 12.7. Диапазон изменения приоритета потока
При наличии нескольких процессоров Windows NT применяет симметричную модель распределения потоков по процессорам (symmetric multiprocessing SMP), Это означает, что любой поток может быть направлен на любой процессор, но программист может ввести некоторые коррективы в эту модель равноправного распределения. Функции SetProcessAffinityMask и SetThreadAffinityMask позволяют указать предпочтения в смысле выбора процессора для всех потоков процесса или для одного определенного потока.
Потоковое предпочтение (thread affinity) вынуждает систему выбирать процессоры только из множества, указанного в маске. Существует также возможность для каждого потока указать один предпочтительный процессор. Это делается с помощью функции SetThreadidealProcessor. Это указание служит подсказкой для планировщика заданий, но не гарантирует строгого соблюдения.