Примечание
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Предупреждение
По состоянию на Windows 11 планирование пользовательского режима не поддерживается. Все вызовы завершаются ошибкой ERROR_NOT_SUPPORTED
.
Планирование в пользовательском режиме (UMS) — это упрощенный механизм, который приложения могут использовать для планирования собственных потоков. Приложение может переключаться между потоками UMS в пользовательском режиме без участия системного планировщика и может восстановить контроль над процессором, если поток UMS блокируется в ядре. Потоки UMS отличаются от волокон в том, что каждый поток UMS имеет собственный контекст потока вместо совместного использования контекста потока одного потока. Возможность переключения между потоками в пользовательском режиме делает UMS более эффективной, чем пулы потоков при управлении большим количеством коротких рабочих элементов, требующих небольшого числа системных вызовов.
UMS рекомендуется для приложений с высокими требованиями к производительности, которые должны эффективно выполнять множество потоков одновременно в многопроцессорных или многоядерных системах. Чтобы воспользоваться преимуществами UMS, приложение должно реализовать компонент планировщика, который управляет потоками UMS приложения и определяет, когда они должны выполняться. Разработчики должны учитывать, должны ли требования к производительности приложений оправдать работу, связанную с разработкой такого компонента. Приложения с умеренными требованиями к производительности могут лучше обслуживаться, позволяя системному планировщику планировать их потоки.
UMS доступен для 64-разрядных приложений, работающих в AMD64 и Itanium версиях Windows 7 и Windows Server 2008 R2 до Windows 10 версии 21H2 и Windows Server 2022. Эта функция недоступна в Arm64, 32-разрядных версиях Windows или Windows 11.
Дополнительные сведения см. в следующих разделах:
- UMS планировщик
- поток планирования UMS
- рабочие потоки UMS, контексты потоков и списки завершения
- функция точки входа планировщика UMS
- исполнение потока UMS
- Лучшие практики по UMS
Планировщик UMS
Планировщик UMS приложения отвечает за создание, управление и удаление потоков UMS, а также за выбор потока UMS для выполнения. Планировщик приложения выполняет следующие задачи:
- Создает один поток планировщика UMS для каждого процессора, на котором приложение будет запускать рабочие потоки UMS.
- Создает рабочие потоки UMS для выполнения работы приложения.
- Поддерживает собственную очередь потоков для выполнения работы, готовых к запуску, и выбирает потоки для выполнения на основе политик планирования приложения.
- Создает и отслеживает один или несколько списков завершения, где система размещает потоки в очереди по завершении их обработки в ядре. К ним относятся только что созданные рабочие потоки и ранее заблокированные потоки на системном вызове, которые были разблокированы.
- Предоставляет функцию точки входа планировщика для обработки уведомлений из системы. Система вызывает функцию точки входа при создании потока планировщика, когда рабочий поток блокируется на системном вызове, или когда рабочий поток явно передает управление.
- Выполняет очистку ресурсов рабочих потоков, завершивших работу.
- Выполняет упорядоченное завершение работы планировщика при запросе приложения.
Поток планировщика UMS
Поток планировщика UMS — это обычный поток, который преобразовался в UMS путем вызова функции EnterUmsSchedulingMode. Планировщик системы определяет, когда поток планировщика UMS будет выполнять свои функции на основе его приоритета относительно других готовых потоков. Процессор, на котором выполняется поток планировщика, задается привязанностью потока, так же, как для потоков, не являющихся UMS.
Вызывающий объект EnterUms ScheduleMode указывает список завершения и функцию точки входа UmsSchedulerProc для связывания с потоком планировщика UMS. Система вызывает указанную функцию точки входа, когда завершает преобразование вызывающего потока в UMS. Функция точки входа планировщика отвечает за определение соответствующего следующего действия для указанного потока. Дополнительные сведения см. в разделе функция точки входа планировщика UMS, далее в этом разделе.
Приложение может создать один поток планировщика UMS для каждого процессора, который будет использоваться для запуска потоков UMS. Приложение также может задать сходство каждого потока планировщика UMS для определенного логического процессора, который, как правило, исключает несвязанные потоки из запуска на этом процессоре, эффективно резервируя его для этого потока планировщика. Имейте в виду, что настройка привязки потоков таким образом может повлиять на общую производительность системы, лишая ресурсов другие процессы, работающие в системе. Для получения дополнительной информации о привязке потоков см. Несколько процессоров.
Рабочие потоки UMS, контексты потоков и списки завершения
Рабочий поток UMS создается путем вызова CreateRemoteThreadEx с атрибутом PROC_THREAD_ATTRIBUTE_UMS_THREAD и указанием контекста потока UMS и списка завершения.
Контекст потока UMS представляет состояние потока UMS рабочего потока и используется для идентификации рабочего потока в вызовах функций UMS. Он создается путем вызова CreateUmsThreadContext.
Список завершения создается путем вызова функции CreateUmsCompletionList. Список завершения принимает рабочие потоки UMS, которые завершили выполнение в ядре и готовы выполняться в пользовательском режиме. Только система может поставить в очередь рабочие потоки в список завершения. Новые рабочие потоки UMS автоматически помещаются в список завершения, указанный при создании потоков. Ранее заблокированные рабочие потоки также помещаются в список завершения, если они больше не блокируются.
Каждый поток планировщика UMS связан с одним списком завершения. Однако один и тот же список завершения может быть связан с любым количеством потоков планировщика UMS, и поток планировщика может получить контексты UMS из любого списка завершения, на который у него есть указатель.
Каждый список завершения имеет связанное событие, которое сигнализирует системе, когда она добавляет один или несколько рабочих потоков в пустой список. Функция getUmsCompletionListEvent извлекает дескриптор события для указанного списка завершения. Приложение может ожидать несколько событий из списка завершения вместе с другими событиями, которые имеют смысл для приложения.
Функция точки входа диспетчера задач UMS
Функция точки входа планировщика приложения реализуется как UmsSchedulerProc . Система вызывает функцию точки входа планировщика приложения в следующее время:
- При преобразовании потока, отличного от UMS, в поток планировщика UMS посредством вызова EnterUmsSchedulingMode.
- Когда рабочий поток UMS вызывает UmsThreadYield.
- Когда рабочий поток UMS блокируется на системной функции, как системный вызов или ошибка страницы.
Параметр Reason функции UmsSchedulerProc указывает причину вызова функции, с которой начинается выполнение. Если функция точки входа была вызвана из-за создания нового потока планировщика UMS, параметр SchedulerParam содержит данные, указанные вызывающим вызовом EnterUmsSchedulingMode. Если функция точки входа была вызвана из-за получения рабочего потока UMS, параметр SchedulerParam содержит данные, указанные вызывающим UmsThreadYield. Если функция точки входа была вызвана, так как рабочий поток UMS заблокирован в ядре, параметр SchedulerParam имеет значение NULL.
Функция точки входа планировщика отвечает за определение соответствующего следующего действия для указанного потока. Например, если рабочий поток заблокирован, функция точки входа планировщика может запустить следующий готовый рабочий поток UMS.
При вызове функции точки входа планировщик приложения должен попытаться извлечь все элементы из связанного с ним списка завершения, вызвав функцию DequeueUmsCompletionListItems. Эта функция извлекает список контекстов потока UMS, которые завершили обработку в ядре и готовы выполняться в пользовательском режиме. Планировщик приложения не должен запускать потоки UMS непосредственно из этого списка, так как это может привести к непредсказуемому поведению в приложении. Вместо этого планировщик должен получить все контексты потоков UMS, вызвав один раз для каждого контекста функцию GetNextUmsListItem, затем вставить контексты потоков UMS в очередь готовых потоков планировщика и только после этого запускать потоки UMS из очереди готовых потоков.
Если планировщику не нужно ждать нескольких событий, он должен вызвать DequeueUmsCompletionListItems с ненулевым значением параметра времени ожидания, чтобы функция дождалась события списка завершения перед возвратом. Если планировщику нужно дождаться нескольких событий списка завершения, он должен вызывать DequeueUmsCompletionListItems с параметром времени ожидания нулевого значения, чтобы функция возвращала немедленно, даже если список завершения пуст. В этом случае планировщик может явно ожидать наступления событий в списке завершения, например, для этого можно использовать WaitForMultipleObjects.
Выполнение потока UMS
Созданный рабочий поток UMS помещается в очередь в указанный список завершенных заданий и не начнет выполнение, пока UMS планировщик приложения не выберет его для выполнения. Это отличается от потоков, отличных от UMS, которые системный планировщик автоматически планирует выполнять, если вызывающий объект явно не создает приостановленный поток.
Планировщик запускает рабочий поток, вызывая ExecuteUmsThread с контекстом UMS рабочего потока. Рабочий поток UMS выполняется до тех пор, пока не уступает вызовом функции UmsThreadYield, блокируется или завершает работу.
Рекомендации по UMS
Приложения, реализующие UMS, должны соблюдать следующие рекомендации.
- Базовые структуры для контекстов потоков UMS управляются системой и не должны изменяться напрямую. Вместо этого используйте QueryUmsThreadInformation и SetUmsThreadInformation для получения и задания сведений о рабочем потоке UMS.
- Чтобы предотвратить взаимоблокировку, поток планировщика UMS не должен совместно использовать блокировки с рабочими потоками UMS. Сюда входят как блокировки, созданные приложением, так и системные блокировки, которые приобретаются косвенно операциями, такими как выделение из кучи или загрузка DLL-библиотек. Например, предположим, что планировщик запускает рабочий поток UMS, который загружает библиотеку DLL. Поток рабочий получает блокировку загрузчика и блокируется. Система вызывает функцию точки входа планировщика, которая затем загружает библиотеку DLL. Это приводит к взаимоблокировке, так как блокировка загрузчика уже удерживается и не может быть освобождена до тех пор, пока первый поток не разблокируется. Чтобы избежать этой проблемы, делегируйте работу, которая может использовать те же блокировки, что и рабочие потоки UMS, в выделенный рабочий поток UMS или другому рабочему потоку.
- UMS является наиболее эффективным при выполнении большей части обработки в пользовательском режиме. По возможности избегайте системных вызовов в рабочих потоках UMS.
- Рабочие потоки UMS не должны предполагать, что используется системный планировщик. Это предположение может иметь тонкие последствия; например, если поток в неизвестном коде задает приоритет или привязку потока, планировщик UMS может тем не менее переопределить его. Код, предполагающий, что используется системный планировщик, может не вести себя должным образом и может нарушиться при вызове потока UMS.
- Системе может потребоваться заблокировать контекст потока рабочего потока UMS. Например, вызов асинхронной процедуры в режиме ядра (APC) может изменить контекст потока UMS, поэтому контекст потока должен быть заблокирован. Если планировщик пытается выполнить контекст потока UMS, пока он заблокирован, вызов завершится ошибкой. Это поведение так задумано, и планировщик должен быть разработан для повторного доступа к контексту потока UMS.