Управление приоритетами процессов в Windows
18.02.2016
Alex Kornev
Windows Server 2012
Один комментарий
Давайте поговорим о приоритетах Windows процессов. В большинстве случаев «играться» с настройкой приоритетов нет необходимости, но, иногда, грамотный системный администратор может помочь системе более правильно распределить процессорное время между запущенными задачами. Единого рецепта нет, но путем «подбора и перебора» это вполне реализуемо. Где это может понадобиться? Например, в связке 1С-SQL можно дать больше процессорного времени 1С и SQL, как наиболее критичным к ресурсам процессам.
В общем случае, посмотреть и изменить приоритет запущенного процесса можно через Task Manager
Windows NT/2000/7/2008

В Windows 2012 это “закопали» чуть глубже

Как видно из приведенных примеров, вам доступно всего 6 приоритетов (как выяснится позже, это классы приоритетов). Достаточно? Microsoft считает, что да. Но давайте вспомним «легендарную» фразу Билла Гейста, который сказал, что «640 KB of RAM will be enough for everybody”. Но время показало, что это далеко не так. : )
А теперь давайте разберемся, как это есть на самом деле.
На самом деле в Windows существует 32 уровня приоритета, от 0 до 31.
Они группируются так:
- 31 — 16 уровни реального времени;
- 15 — 1 динамические уровни;
- 0 — системный уровень, зарезервированный для потока обнуления страниц (zero-page thread).
При создании процесса, ему назначается один из шести классов приоритетов:
- Real time class (значение 24),
- High class (значение 13),
- Above normal class (значение 10),
- Normal class (значение 8),
- Below normal class (значение 6),
- или Idle class (значение 4).
Посмотреть приоритет процесса, как писалось выше, можно, используя Task Manager.
Приоритет каждого потока (базовый приоритет потока) складывается из приоритета его процесса и относительного приоритета самого потока. Есть семь относительных приоритетов потоков:
- Normal : такой же как и у процесса;
- Above normal : +1 к приоритету процесса;
- Below normal : -1;
- Highest : +2;
- Lowest : -2;
- Time critical : устанавливает базовый приоритет потока для Real time класса в 31, для остальных классов в 15.
- Idle : устанавливает базовый приоритет потока для Real time класса в 16, для остальных классов в 1.
В следующей таблице показаны приоритеты процесса, относительный и базовый приоритеты потока.
Теперь, когда мы все это узнали, что же с этим всем можно сделать? Ну, например, начать использовать.
Как еще можно запустить процесс с «нестандартным» приоритетом или изменить?
Метод 1. Запустить задачу/процесс и изменить приоритет через Task Manager.
- Доступно только 6 приоритетов
- Переключение приоритетов производится мышкой, не автоматизируется.
Метод 2. Можно воспользоваться командой START с соответствующими ключами
Доступные ключи, отвечающие за приоритеты, следующие (я умышленно опускаю ключи командной строки команды START не имеющие отношения к описываемому процессу работы с приоритетами):
C:\>start /?
Starts a separate window to run a specified program or command.
START [«title»] [/D path] [/I] [/MIN] [/MAX] [/SEPARATE | /SHARED]
[/LOW | /NORMAL | /HIGH | /REALTIME | /ABOVENORMAL | /BELOWNORMAL]
[/NODE <NUMA node>] [/AFFINITY <hex affinity mask>] [/WAIT] [/B]
[command/program] [parameters]
LOW Start application in the IDLE priority class.
NORMAL Start application in the NORMAL priority class.
HIGH Start application in the HIGH priority class.
REALTIME Start application in the REALTIME priority class.
ABOVENORMAL Start application in the ABOVENORMAL priority class.
BELOWNORMAL Start application in the BELOWNORMAL priority class.
Как видим, команда START дает возможность запустить процесс все с теми же 6-ю приоритетами, которые доступны через Task Manager
- Доступно только 6 приоритетов
Метод 3. Использование утилиты wmic.exe
Как было показано выше, Task Manager, и команда START достаточно неуклюжи для задачи назначения приоритетов. Посмотрим, как это применять более гибко. Будем использовать утилиту wmic.exe.
wmic process where name=»AppName» CALL setpriority ProcessIDLevel
wmic process where name=»calc.exe» CALL setpriority 32768
wmic process where name=»calc.exe» CALL setpriority «above normal»
- idle: 64
- below normal: 16384
- normal: 32
- above normal: 32768
- high priority: 128
- real time: 256
Вот короткий пример запуска wmic.exe для получения необходимой информации
wmic process list brief
Вы получите список процессов, запущенных на вашем локальном компьютере. Теперь выполните команду:
wmic process list brief | find «cmd.exe»

Специально запустил несколько копий cmd.exe, чтобы иллюстрация была более полной.
Теперь список процессов ограничен только теми процессами, в имени исполняемого модуля которых присутствует строка «cmd.exe». Обратите внимание на PID процесса(ов).
Теперь давайте попробуем отобрать интересующие нас процессы, используя непосредственно WMI и не прибегая к стандартным средствам командной строки. Для этого просто напишите:
wmic process where description=’cmd.exe’ list brief

Сравните полученные результаты. Запомните PID процесса CMD.EXE.
Командная строка для запуска wmic.exe
wmic process where processid=’XXXX’ CALL setpriority ProcessIDLevel
Ну а теперь можем изменить приоритет конкретного процесса (например с PID=8476):
wmic process where processid=’8476′ CALL setpriority 32768
wmic process where processid=’8476′ CALL setpriority «above normal»
А что дальше? Прикидывать, пробовать, подбирать и тонко регулировать приоритеты. Улучшая работу сервисов и процессов, а также работу конечных пользователей.
Предыдущая статья Следующая статья
Как задать приоритет процесса Windows 11 и Windows 10

В диспетчере задач Windows 11 и Windows 10 присутствует возможность вручную задать приоритет выполняемых процессов: эту возможность можно использовать в целях снижения нагрузки, вызываемой «необязательными» процессами и потенциального увеличения производительности для тех программ, для которых она важна.
В этой инструкции подробно о том, как задать приоритет процессов в диспетчере задач, какие параметры приоритета доступны и что означает каждый из них.
Изменение приоритета процессов
Для того, чтобы задать приоритет определенного процесса в Windows 11 или Windows 10, достаточно выполнить следующие шаги:
- Откройте диспетчер задач. Это можно сделать из контекстного меню по правому клику на кнопке «Пуск». Также можно нажать клавиши Ctrl+Shift+Esc или нажать клавиши Win+R на клавиатуре, ввести taskmgr и нажать Enter.

- В диспетчере задач перейдите на вкладку «Подробности». Если она не отображается, сначала нажмите по кнопке «Подробнее» внизу окна диспетчера задач.
- Нажмите правой кнопкой мыши по процессу, приоритет которого следует изменить.
- Выберите в контекстном меню пункт «Задать приоритет» и установите одно из значений: Реального времени, Высокий, Выше среднего, Обычный, Ниже среднего, Низкий.

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

Что касается доступных для выбора вариантов приоритета:
- Реального времени — самый высокий доступный уровень приоритета процессов. Задачи процесса, для которого выставлен этот приоритет, выполняются в первую очередь. Учитывайте: если задать этот уровень для вашей игры или программы, это может привести к сбоям в работе системы (из-за накопившихся, но не выполненных задач другими процессами).
- Высокий — следующий уровень, по умолчанию задан для важных системных процессов, от которых зависит стабильность работы системы. Можно установить для пользовательских программ, но иногда может привести к сбоям.
- Выше среднего — сравнительно спокойно можно использовать для того, чтобы слегка повысить производительность вашей игры или программы, не навредив работе системных процессов.
- Обычный — приоритет, с которым работают большинство приложений по умолчанию.
- Ниже среднего — можно использовать для процессов, завершить которые нельзя, но время выполнения которых не играет роли.
- Низкий — самый низкий из возможных приоритетов. Ресурсы для задач с этим уровнем выделяются только после выполнения других активных задач.
При изменении приоритета фоновых системных процессов настоятельно рекомендую помнить, что именно вы меняли, чтобы иметь возможность вернуть параметры в значения по умолчанию в случае, если в работе системы будут замечены какие-либо проблемы.
Что происходит при изменении приоритета процессов и стоит ли его менять
Изменение приоритета процессов в диспетчере задач изменяет очередь задач этого процесса в общей очереди всех задач. Задачи с более высоким приоритетом обрабатываются процессором в первую очередь, необходимые системные ресурсы для них также выделяются в приоритетном порядке.
Что касается осмысленности изменения приоритетов процессов:
- Иногда с помощью таких изменений можно добиться повышения производительности нужного ПО.
- Меняя приоритеты системных процессов на более низкие легко получить результат в виде системных сбоев. Не следует задавать приоритет для процессов в массовом порядке.
- Зависания и сбои можно также получить, задав приоритет «Реального времени» для «тяжелых» игр и программ, либо выставив высокие приоритеты сразу для нескольких одновременно выполняемых пользовательских программ.
И ещё один момент: если ваша задача — повысить производительность в играх, включение встроенного игрового режима в параметрах Windows 11 и Windows 10 (раздел «Игры») точно будет безопаснее и в некоторых случаях может дать более заметный эффект, чем настройка приоритетов выполняемых процессов.
Видео
А вдруг и это будет интересно:
19.08.2021 в 17:33
А вот допустим хочу я хрому приоритет задать, а там висят 17 штук chrome.exe
Это получается каждому exe задавать ручками необходимый приоритет?
И ведь эту процедуру необходимо повторять будет после каждого закрытия\открытия программы?
19.08.2021 в 20:21
Да, всё так как вы описываете, так что это не настройка «для бытового использования».
Кстати, если хром у вас тормозит, изучите его собственный диспетчер задач (Shift+Esc в открытом хроме), возможно обнаружите там что-нибудь.
19.08.2021 в 21:00
Дмитрий, вопрос не по данной теме. Помоги, пожалуйста, решить одну проблему. У меня на компе два дисковода (CD и DVD-ROM) и ни один из них не читает и не пишет диски. Копм отображает их, службы показывают, что они работоспособны и исправны, с драйверами всё в порядке. Но смотришь в «Свойства» и видишь запись «Нет доступа». И дело не в антивирусе и даже не программе виртуального дисковода (их я отключал). Дело в реестре, в его правке.
Выходил на форумы. Оказывается, эта проблема у многих пользователей и в разных системах (Windows 10, 8.1, 7). Где-то пишут, что это системный сбой. Но 1,5 года назад я пользовался этими дисководами без проблем (писал и читал диски). Что случилось с ними и на каком этапе, не знаю.
На одном из форумов два пользователя решили эту проблему через реестр, но переписка между ними была не совсем мне понятна. Сообщали очень кратко и не понятно. Я считаю, что эта проблема не из легких. Если удастся, рассмотри эту проблему, напиши статью или посоветуй что-нибудь. Кстати, если у тебя комп или ноутбук с дисководом, возможно и у тебя существует эта проблема. Спасибо заранее.
20.08.2021 в 19:27
ПК с приводом и последней 10-кой у меня-то есть, но проблемы такой не наблюдаю…. а где-то еще не столкнулся.
С реестром нашел предложение решения… в общем-то можно не вручную править, а в командной строке от имени администратора ввести и выполнить:
и перезагрузиться. Вот только что-то я не уверен, что оно будет работать.
Более того, я даже на все 100 не уверен, что это точно программная проблема (и сам бы, например, протестировал их же на другом ПК).
14.11.2021 в 17:30
Как сохранить выставленный\е приоритеты, что бы после перезагрузки, система выставляла нужный\е приоритет нужному процесс, исполняемому файлу?
20.04.2022 в 05:04
Есть ли способ задать системные прерывания или приоритет процесса таким процессам как: System, Системные прерывания. Мне бы хотелось распределить задачи на отдельные потоки. Нужно сделать так, чтоб системные процессы работали в определенных потоках
17.10.2022 в 18:10
ПОМОГИТЕ ПЛИЗ!
Когда я поставил приоритет, через несколько секунд в приложении он становиться обратно на стандарт, почему так и как это остановить?
2.5. Понятие приоритета и очереди процессов
Приоритет — свойство процесса, определяющее важность необходимость выполнения.
Назначение приоритетов выполняется пользователем либо администратором системы, возможно также программное изменение приоритета процесса. На выбор оптимального уровня приоритета влияют в основном два соображения: важность, ответственность данного процесса либо привилегированное положение запускающего процесс пользователя
количество процессорного времени, на которое будет претендовать процесс как мы видели в примере с фоновой печатью, высокий приоритет процесса, мало загружающего процессор, почти не приводит к замедлению работы остальных процессов.
Основной алгоритм приоритетного планирования напоминает простое круговое планирование, однако круговая очередь активных процессов формируется отдельно для каждого уровня приоритета. Пока есть хоть один активный процесс в очереди с самым высоким приоритетом, процессы с более низкими приоритетами не могут получить управление. Только когда все процессы с высшим приоритетом заблокированы либо завершены, планировщик выбирает процесс из очереди с более низким приоритетом.
Приоритет, присваиваемый процессу при создании, называется статическим приоритетом. Дисциплина планирования, использующая только статические приоритеты, имеет один существенный недостаток: низкоприоритетные процессы могут надолго оказаться полностью отлученными от процессора. Иногда это приемлемо если высокоприоритетные процессы несравнимо важнее, чем низкоприоритетные, однако чаще хотелось бы, чтобы и на низкие приоритеты хоть что-нибудь перепадало, пусть даже реже и в меньшем количестве, чем на высокие. Для решения этой задачи предложено множество разных алгоритмов планирования процессов, основанных на идее динамического приоритета.
2.6. Синхронизация процессов
Синхронизация процессов — приведение двух или нескольких процессов к такому их протеканию, когда определённые стадии разных процессов совершаются в определённом порядке, либо одновременно.
Синхронизация необходима в любых случаях, когда параллельно протекающим процессам необходимо взаимодействовать. Для её организации используются средства межпроцессного взаимодействия. Среди наиболее часто используемых средств — сигналы и сообщения, семафоры и мьютексы, каналы англ. pipe, совместно используемая память.
2.7. Средства обработки сигналов
Сигналы представляют собой средство уведомления процесса о наступлении некоторого события в системе. Инициатором посылки сигнала может выступать как другой процесс, так и сама ОС. Сигналы, посылаемые ОС, уведомляют о наступлении некоторых строго предопределенных ситуаций. при этом каждой такой ситуации сопоставлен свой сигнал. Кроме того, зарезервирован один или несколько номеров сигналов, семантика которых определяется пользовательскими процессами по своему усмотрению.
Сигналы являются механизмом асинхронного взаимодействия, т.е. момент прихода сигнала процессу заранее неизвестен. Однако, процесс может предвидеть возможность получения того или иного сигнала и установить определенную реакцию на его приход. В этом плане сигналы можно рассматривать как программный аналог аппаратных прерываний.
При получении сигнала процессом возможны три варианта реакции на полученный сигнал: Процесс реагирует на сигнал стандартным образом, установленным по умолчанию для большинства сигналов действие по умолчанию — это завершение процесса Процесс может установить специальную обработку сигнала, в этом случае по приходу сигнала вызывается функция-обработчик, определенная процессом при этом говорят, что сигнал перехватывается Процесс может проигнорировать сигнал
Для каждого сигнала процесс может устанавливать свой вариант реакции, например, некоторые сигналы он может игнорировать, некоторые перехватывать, а на остальные установить реакцию по умолчанию. При этом в процессе своей работы процесс может изменять вариант реакции на тот или иной сигнал. Однако, необходимо отметить, что некоторые сигналы невозможно ни перехватить, ни игнорировать. Они используются ядром ОС для управления работой процессов
Если в процесс одновременно доставляется несколько различных сигналов, то порядок их обработки не определен. Если же обработки ждут несколько экземпляров одного и того же сигнала, то ответ на вопрос, сколько экземпляров будет доставлено в процесс — все или один — зависит от конкретной реализации ОС.
Расследование: что выше, чем приоритеты потоков в Windows?
Это расследование, как и многие другие, началось с того, что я занимался собственными делами, не пытаясь искать себе проблем. На этот раз всё, что я сделал — открыл крышку ноутбука и попытался выполнить вход в систему.
В первые несколько раз, когда это приводило к задержке в двадцать секунд, я игнорировал проблему, надеясь, что она решится сама собой. В следующие несколько раз я задумался о расследовании, но проблемы производительности, возникающие ещё до того, как ты вошёл в систему, сложнее решать, а мне было лениво.
Когда я заметил, что избегаю закрывать ноутбук, потому что меня пугают эти слишком частые задержки, то понял, что пора заняться этим серьёзно.
К счастью, я недавно исправил трассировку кольцевого буфера UIforETW, сделав его надёжным, поэтому я запустил его и начал ждать следующего случая задержки. Долго ожидать мне не пришлось.
Мне потребовалось несколько раз, чтобы получить полностью устраивающую меня трассировку ETW. И поскольку эта территория была для меня незнакомой, на выяснение происходящего потребовалось какое-то время. Я по-прежнему не полностью разобрался в проблеме, но на 90% понял причины её возникновения. Мне удалось многое узнать, в том числе некоторые новые подробности о Windows-планировщике, а также я обнаружил абсолютно эффективное решение.
Идеальная трассировка, которую я в конце концов записал, при загрузке в Microsoft Windows Performance Analyzer (WPA) выглядит так:

Стандартные события, окна в фокусе и использование ЦП
Эта таблица и два графика содержат кучу информации. Верхняя таблица (Generic Events) показывает записанные UIforETW нажатия клавиш. Я пытался нажимать клавишу (код виртуальной клавиши 162) раз в секунду, пока не появится поле ввода пароля. Так как эти 17 нажатий клавиши выбраны, в графике ниже они показаны вертикальными синими линиями для упрощённой визуализации времени выполнения критически важных событий. По оси x отложено время в секундах.
Горизонтальные полосы верхнего графика (Window in Focus) показывают, какой процесс имеет фокус на протяжении этого времени. Всего есть шесть разных процессов. Период Tracing off — это краткое время, в течение которого был закрыт ноутбук.
На нижнем графике показано использование ЦП. Информация получается по данным переключения контекста, поэтому должна быть совершенно точной и полной. В этой трассировке значение 100% показывает момент, когда использовались все восемь логических процессоров моего четырёхъядерного восьмипоточного ноутбука.
Получив данные трассировки, я должен был разобраться, чем же втайне занимается мой ноутбук, когда закрыта крышка и до момента, когда я снова вернусь в систему.
Буря перед затишьем
Как мы видим, ноутбук в начале трассировки ноутбук находится в относительном простое, как и должен быть. Затем я закрыл его крышку. Похоже, это вызвало всплеск активности ЦП и изменения фокуса окон. Window in Focus изменилось с UIforETW на Idle, потом на csrss, обратно на Idle, на LogonUI, а затем снова на Idle. Кто бы мог подумать?
В течение этого интервала ноутбук выполнял примерно 17 секунд ЦП различных видов обработки. Часть её является работой, необходимой для отключения. Часть — это программы (в том числе и внутренние инструменты Google), которые зарегистрированы в Планировщике заданий для выполнения «При блокировании рабочей станции любым пользователем» — вполне логично. Я даже заметил, что выполняется работа по созданию элементов UI для выполнения входа в систему, когда пользователь продолжит работу — нужно ведь быть готовыми заранее, правда?
17 секунд ЦП — довольно долгое время для перехода ноутбука в сон. Даже на моём ноутбуке с четырьмя ядрами и восемью потоками процесс занимает больше четырёх секунд. На моём домашнем ноутбуке для засыпания требуется больше 13 секунд времени ЦП, и почти все они уходят на код Windows. Службе политики диагностики действительно нужно выполнять пару SruDbTableSearches прежде, чем ноутбук сможет отдохнуть?
Думаю, эта излишняя работа при переходе в сон — тоже проблема, но это не та самая проблема, которую я ищу. Поэтому я просто решил от неё отвернуться.
И только гораздо позже я осознал, что именно в течение этого времени были брошены зёрна разрушения моего бага…
После блокировки ноутбука активность ЦП отсутствует. В этом конкретном тесте ноутбук был заблокирован в течение примерно 16 секунд.
Судорожное пробуждение
Активность ЦП при переходе в сон несравнима с тем, когда он начал пробуждаться. В течение этого времени мой перегруженный ноутбук выполнял примерно 172 секунд времени ЦП (. ) в течение 22,6 секунды. Это большой объём работы.
Одна из загадок этого процесса заключается в падении использования ЦП почти до нуля спустя примерно секунду после первоначальной вспышки активности. Этот кратковременный промежуток простоя кажется довольно аномальным, учитывая окружающий его хаос. Но я думаю, что эта особенность не связана с проблемой, поэтому не стал обращать на неё внимания.
Ещё одна загадка заключается в том, почему столько программ оживает после этой краткой паузы. Забавно, что самым серьёзным нарушителем, ответственным за 31,6 из 172 секунд ЦП, стал Windows Performance Analyzer (WPA) — та самая программа, которую я использую для анализа трассировок. Три копии, которые я оставил запущенными, упорно трудятся над рендерингом своего UI, несмотря на то, что он пока не виден.
Кроме того, тёмные паттерны возникают при попытке инициализации ноутбуком устройств. KeStallExecutionProcessor — это ждущий цикл, и было странно видеть, что это самая исполняемая функция всей системы. Неужели секунда с лишним ждущего цикла — единственный способ запуска оборудования? 700 мс времени ЦП действительно обязательно тратить на инициализацию мыши и клавиатуры? Должны ли Microsoft и Intel игнорировать рекомендацию Microsoft о максимуме в 50 микросекунд?

Драйверы ждущего цикла. i8042prt.sys написан Microsoft. Два следующих созданы Intel.
В конечном итоге, за это время активно выполняется множество программ. Похоже, большинство из них сталкивается с той же проблемой, что и WPA — отчаянно пытается отрисовать пиксели на скрытом экране, и это намекает на баг Windows. Но даже без этого бага explorer.exe и другие программы энергично стремятся что-то делать. Но в конце концов, хотя это избыточное использование ЦП является необходимой частью проблемы, это не сама проблема. Так что я снова перестал обращать на неё внимание.
Фокус
При анализе трассировок важно выяснить, когда происходят важные действия. Основными уликами стали события ввода, потому что я прекратил нажимать на control после появления формы ввода пароля. Вот последние три нажатия на клавишу control в приближенном виде на графике Window in Focus:

Похоже, что критически важными событиями являются получение фокуса LockApp.exe, после которого фокус почти мгновенно получает LogonUI.exe. Предположительно, я ввёл пароль в LogonUI.exe (удобно, что трассировка не перехватила события клавиатуры), после чего фокус кратковременно переключился на explorer, а затем на UIforETW, с которого я начал.
Также похоже на то, что LogonUI.exe не может получить фокус до LockApp.exe — этот паттерн повторяется во всех трассировках, которые я изучал.
Итак, спустя больше тысячу слов, посвящённых решению этой загадки, у нас наконец сформировался чёткий вопрос, который мы можем исследовать: почему прежде чем LockApp.exe получит фокус после выхода из простоя, проходит двадцать секунд?
У нас есть вопрос? Отлично, давайте на него ответим
При помощи полученных из переключения контента данных CPU Usage (Precise) я быстро обнаружил, что в течение двадцати секунд после пробуждения LockApp.exe получил менее одной миллисекунды времени ЦП, а в течение более чем 14 секунд (с 35,158 с до 49,827 с) не работал вообще:

LockApp долгое время вообще не работает
Если процесс или поток какое-то время не выполнялся, и вы хотите узнать, почему, то обычно важные улики можно найти в первом переключении контекста после долгого затишья, а именно переключения на 49,827 секунды трассировки. Я изменил порядок столбцов, чтобы показать больше данных этого переключения контекста:

LockApp подготовлен, но не выполняется. Странно.
Count, равное 1 означает, что мы просматриваем данные для одного переключения контекста.
Time Since Last, равное 38.2 миллионам микросекунд означает, что этот поток не выполнятся в течение 38,2 секунд. Само по себе это не хорошо и не плохо. Простаивающие потоки экономят энергию, да и в конце концов ноутбук какое-то время находился во сне.
Switch-In Time просто сообщает нам, когда точно поток помещается в ЦП — когда выполняется переключение контекста на этот поток.
И теперь мы переходим к столбцу Ready. Он сообщает нам, как долго поток был готов к выполнению, но не выполнялся. Иными словами, этот поток чего-то ждал (блокировки, дескриптора) и это что-то было освобождено или инициировано, но поток по-прежнему не выполнялся в течение 19,493 секунды.
Чтобы лучше понять столбец Ready (us), можно взглянуть на столбец Ready Time (s). Он говорит нам, когда поток подготовлен. Мы видим, что на 30,333 секунды трассировки этот поток был подготовлен к выполнению, но не выполнялся до 49,827 секунды. Похоже, это важно.
Такое расположение столбцов иначе показывает нам то же переключение контекста:

New Thread Stack и Ready Thread Stack
Итак, этому потоку (который, как показывает New Thread Stack, ожидал NtWaitForWorkViaWorkerFactory) было приказано проснуться (системным процессом, вызывающим KeSetEvent) вскоре после того, как я открыл крышку ноутбука, на 30.333 секунды трассировки. Но он запустился не тогда (что было бы «хорошо»), а спустя 19,494 с, и это плохо.
Обычно при проведении подобного анализа ожидания я трачу много времени на то, чтобы разобраться, почему поток ожидает, и что заставило его не быть готовым. Но я сейчас впервые выполнял анализ ожидания, при котором это было не важно, и вопрос заключался в том, почему этот готовый поток не выполняется.
Большинство людей не тратит так много времени на изучение трассировок ETW, поэтому здесь нужно пояснение. Это очень странно. Если поток готов, то он обычно запускается мгновенно, или спустя несколько миллисекунд. Готовность потока, как можно понять из названия, означает, что поток готов к выполнению и почти ничто не может ему помешать. Но давайте разберёмся в том, что может предотвратить выполнение готового потока.
Приоритет потоков
Сначала я предположил, что это простой случай «голода» ЦП. Время ЦП требуется десяткам процессов, и из-за этого LockApp не получает нужного, пока нагрузка не снизится. Однако эта теория не совсем соответствует симптомам, потому что процессу LockApp удалось продолжаться примерно 18 секунд вообще не получая времени ЦП.
Теория «голода» ЦП хороша тем, что она проверяема. Мне удалось повысить приоритет процесса LockApp при помощи Диспетчера задач (во время одного из кратких промежутков времени, когда он не был приостановлен системой UWP), поэтому в окончательной трассировке, которую я использовал для этого поста, LockApp выполнялся с высоким приоритетом. Обычный поток Windows работает с приоритетом примерно 8-10. Наибольший приоритет, с которым может выполняться обычный (не реального времени) поток Windows — 15. Мои трассировки ETW показали, что LockApp всегда работал с приоритетом 13 или выше.
Вот график времени ЦП для критических 19,494 секунды, сгруппированный и раскрашенный по приоритету потока (New In Pri, настоящего приоритета, который был назначен потоку). Мы видим, что потоки с приоритетами 4, 8, 9 и 10 потребляют подавляющее большинство времени ЦП, особенно в конце:

Использование ЦП по приоритету
Вот ещё одно изображение со скрытыми потоками с приоритетами 0-12. Каждый раз, когда график падает ниже 12,5% (что обозначает один логический процессор времени ЦП моего восьмипоточного ноутбука), LockApp обязательно должен запускаться, и становится совершенно невероятно, что приоритет мешает ему выполняться так часто, когда множество потоков с меньшим или равным приоритетом получают кучу времени.

Использование ЦП по приоритету, только потоки с высоким приоритетом
Устранение инвертирования приоритетов
Есть предположение, что алгоритмы инвертирования приоритетов Windows настолько способствуют другим потокам, что LockApp.exe блокируется. Но так как показанные выше графики демонстрируют, что в принятии решений о планировании используются истинные приоритеты, от этого (всегда малоубедительного) предположения придётся отказаться.
Выгрузка ядра стека
Когда я рассказал об этой головоломке в Twitter, один из комментаторов предположил, что был выгружен стек ядра потока. Я не был знаком с этой ситуацией, но после объяснений Джона Верта (он понимает в своём деле) я отключил подкачку стека ядра и перезагрузил компьютер. Ничего не изменилось. На самом деле, я и не думал, что это поможет, учитывая, что у меня 32 ГБ памяти, а проблема возникает многократно и часто; но лучше было точно в этом убедиться.
Приостановка процесса
Так как LockApp — это современное UWP-приложение, оно подвержено ограничениям, схожим с теми, которые свойственны приложениям для смартфонов. Среди прочего это означает, что оно может быть приостановлено, когда не находится на переднем плане, а затем «разморозиться» при возврате на передний план. Джеймс Форшоу предложил записать провайдер Microsoft-Windows-Kernel-Process ETW, чтобы получить данные об этом.
События спроектированы так, чтобы вызывать максимальную растерянность. Название задачи Process Freeze используется и для «размораживания» и для «замораживания», а версия события win:Stop означает, что процесс запускается (прекратил замораживаться), а версия win:Start означает, что процесс останавливается (начинает замораживаться). Всё это чрезвычайно логично, но сильно сбивает с толку. Если бы названия событий были разделены на Freeze и Thaw, то путаницы получилось бы меньше.
По этим событиям нет документации, но благодаря анализу я определил, что эти события всегда создаются Background Tasks/Broker Infrastructure Service. Название и process ID соответствующего процесса указываются в поле FrozenProcessID.

События ProcessFreeze (также используемые для размораживания)
Исследовать этот провайдер было интересно — у него есть множество многообещающих событий — но в конечном итоге оказалось, что во время трассировки LockApp не приостанавливался и не размораживался. Однако этот провайдер показался достаточно полезным, поэтому я модифицировал UIforETW, чтобы будущие версии всегда его записывали.
Мы уже всё исключили
Ни одна из описанных выше теорий не казалась мне сильно вероятной, и теперь мы их все исключили. Я начал искать помощи, и попросил подкинуть мне идей друга из Microsoft. И в этот момент я обнаружил, что так хорошо известный в Windows приоритет потока 0-31 на самом деле является всего лишь пятью битами низкого приоритета полной системы приоритетов.
Использование служебного положения
Оказалось, что моё невежество было моей собственной виной. Если бы я внимательно прочитал все 108 страниц раздела Threads книги Windows Internals, 7th Edition, Part 1, то я бы понял, что происходит. Если вы хотите заскочить вперёд, то эта тема раскрывается на страницах с 287 по 295.
Это поле сверхприоритета, о котором я не знал, называется Rank. Оно отображается в WPA как скрытый по умолчанию столбец (чтобы найти его, придётся открыть View Editor) под названием NewThreadRank. При планировании потоков Thread Rank обладает преимуществом перед приоритетом. Почти все потоки имеют Rank 0, а поток с Rank 0 всегда имеют более высокий приоритет, чем поток с Rank 2. Включив столбец NewThreadRank и взглянув на левую часть таблицы, мы сразу же можем увидеть проблему:

Ранг важнее, чем приоритет
Потоки LockApp.exe имеют Rank 2 и это значит, что они, несмотря на приоритет 14, по сути обладает наименьшим приоритетом в системе.
Почти полное объяснение
Так как оказалось, что потоки LockApp.exe имеют Rank 2, они могут выполняться только когда ни один из потоков с Rank 0 «не хочет» выполняться. Поскольку многие приложения (по непонятным причинам) активно рендерят свои невидимые экраны, они сражаются за каждую крошку времени ЦП, не оставляя ничего для более высоких значений рангов. Как только LockApp.exe получает крошечную долю времени ЦП, он быстро перемещается в Rank 0 (а нагрузка на ЦП падает), после чего процесс входа в систему выполняется обычным образом.
Узнав эту информацию, я начал изучать, как меняется со временем ранг LockApp. В последние несколько секунд перед уходом в сон ноутбука LockApp внезапно перешёл с ранга 0 на 2. Ранг предназначен для того, чтобы ЦП не позволял процессам занимать слишком много времени, как например в том случае, когда Windows Photos слишком увлекается непрошеной фоновой обработкой и выполняет переход с ранга 2 до 19:

Microsoft.Photos спускается по рангу
Из документации можно понять, что основное предназначение ранга потока — справедливое разделение времени ЦП между сессиями на машине, чтобы процессы одного пользователя не вредили другим. Оба эти варианта использования ранга дают понять, что ранг потока должен повышаться, только если тот использует много времени ЦП, а когда ноутбук отправлялся в сон, LockApp.exe использовал всего 79,3 мс времени ЦП, а остальная часть системы — 17 с времени ЦП. И тем не менее, ОС почему-то решила понизить ранг LockApp до 2 в процессе перехода ко сну.
ОС изменяет ранг потока, только если он относится к «группе планирования» (KSCHEDULING_GROUP), а большинство потоков в обычной установке Windows не являются её членами. Следовательно, большинство потоков не подвергается изменению ранга, поэтому они могут тратить время ЦП так, как им захочется.
Оставшиеся загадки
К сожалению, до сих пор непонятно, почему перед включением сна LockApp.exe понижается до Rank 2. Я предположу, что LockApp находится в группе планирования, и, вероятно, один из алгоритмов ведёт себя неправильно. Но мне не удалось найти API для расследования этого, да и время поджимало. Если вам известны какие-то подробности, то напишите в комментариях к оригиналу статьи. Сам принцип использования ранга как наиболее важного компонента в принятии решений о планировании должен, как мне кажется, неизбежно поломаться, если большинство процессов системы в нём не задействовано — потоки в группах планирования всегда рискуют остаться без нужных ресурсов. Планирование динамического распределения ресурсов (DFSS) обречено на провал, если большинство потоков в нём не участвует.
Также я не знаю, почему после ухода в сон остаются активными так много приложений. Обычно это объясняют тем, что «многие таймеры заканчиваются, когда ноутбук пробудет в режиме сна несколько часов», но такое объяснение не подходит, если ноутбук находился во сне всего несколько секунд, а поведение рендеринга WPA указывает на то, что в оконной системе происходит что-то неправильное. Добавьте к этому приложения с плохим поведением и драйверы ждущего цикла, и всё суммируется в долгое время ЦП.
То, что утихание бури ЦП и запуск LockApp происходят одновременно, приводит к очевидному объяснению: LockApp может работать только тогда, когда потребность в ЦП падает. Но есть и столь же убедительное объяснение: как только LockApp получает возможность запуска (или, возможно, её получает LogonUI), потребность в ЦП падает. Подходят оба объяснения, но я думаю, что больше правдоподобно второе, потому что в противном случае мы не можем объяснить, почему внезапно прекращается кажущийся бесконечным рендеринг WPA.
Решение проблемы
Как только я понял, что LockApp.exe — это отдельное приложение, испытывающее проблемы с запуском, и что повышение его приоритета не помогает, я отключил его. В этом мне помог такой файл DisableLockScreen.reg:
Благодаря отключению экрана блокировки ноутбук приходит в себя сразу после открывания крышки. Я не заметил ни торможения, ни бурь ЦП, а для входа теперь требуется на один шаг меньше.
В первом посте в twitter, который я опубликовал, когда впервые столкнулся с проблемой, содержится временная шкала расследования, которая может оказаться кому-то полезной. Кроме того, в пост пришло много умных людей из twitter, спасибо им.
Когда я вернулся к статье, то выяснил, что после включения обратно экрана блокировки проблема исчезла. Простая перезагрузка не устранила её — в феврале я много раз перезагружался, но мы, наверно, так и не узнаем, почему она пропала.