pianificazione User-Mode

Avviso

A partire da Windows 11, la pianificazione in modalità utente non è supportata. Tutte le chiamate hanno esito negativo con l'errore ERROR_NOT_SUPPORTED.

La pianificazione in modalità utente (UMS) è un meccanismo leggero che le applicazioni possono usare per pianificare i propri thread. Un'applicazione può passare tra thread UMS in modalità utente senza coinvolgere l'utilità di pianificazione del sistema e recuperare il controllo del processore se un thread UMS si blocca nel kernel. I thread UMS differiscono dalle fibre in quanto ogni thread UMS ha un proprio contesto di thread anziché condividere il contesto del thread di un singolo thread. La possibilità di passare da un thread all'altro in modalità utente rende la messaggistica unificata più efficiente rispetto ai pool di thread per la gestione di un numero elevato di elementi di lavoro a breve durata che richiedono poche chiamate di sistema.

La messaggistica unificata è consigliata per le applicazioni con requisiti di prestazioni elevati che devono eseguire in modo efficiente molti thread contemporaneamente in sistemi multiprocessore o multicore. Per sfruttare i vantaggi della messaggistica unificata, un'applicazione deve implementare un componente dell'utilità di pianificazione che gestisce i thread UMS dell'applicazione e determina quando devono essere eseguiti. Gli sviluppatori devono considerare se i requisiti di prestazioni dell'applicazione giustificano il lavoro coinvolto nello sviluppo di un componente di questo tipo. Le applicazioni con requisiti di prestazioni moderate potrebbero essere meglio gestite consentendo all'utilità di pianificazione del sistema di pianificare i thread.

UMS è disponibile per le applicazioni a 64 bit in esecuzione in versioni AMD64 e Itanium di Windows 7 e Windows Server 2008 R2 fino a Windows 10 versione 21H2 e Windows Server 2022. Questa funzionalità non è disponibile in Arm64, versioni a 32 bit di Windows o in Windows 11.

Per informazioni dettagliate, vedere le sezioni seguenti:

Utilità di pianificazione di messaggistica unificata

L'utilità di pianificazione UMS di un'applicazione è responsabile della creazione, della gestione e dell'eliminazione di thread UMS e della determinazione del thread UMS da eseguire. L'utilità di pianificazione di un'applicazione esegue le attività seguenti:

  • Crea un thread dell'utilità di pianificazione di messaggistica unificata per ogni processore in cui l'applicazione eseguirà thread di lavoro UMS.
  • Crea thread di lavoro di messaggistica unificata per eseguire il lavoro dell'applicazione.
  • Gestisce la propria coda ready-thread di thread di lavoro pronti per l'esecuzione e seleziona i thread da eseguire in base ai criteri di pianificazione dell'applicazione.
  • Crea e monitora uno o più elenchi di completamento in cui i thread di sistema vengono accodato al termine dell'elaborazione nel kernel. Questi includono thread di lavoro appena creati e thread bloccati in precedenza in una chiamata di sistema che diventano sbloccati.
  • Fornisce una funzione del punto di ingresso dell'utilità di pianificazione per gestire le notifiche dal sistema. Il sistema chiama la funzione del punto di ingresso quando viene creato un thread dell'utilità di pianificazione, un thread di lavoro si blocca in una chiamata di sistema o un thread di lavoro restituisce in modo esplicito il controllo.
  • Esegue attività di pulizia per i thread di lavoro che hanno terminato l'esecuzione.
  • Esegue un arresto ordinato dell'utilità di pianificazione quando richiesto dall'applicazione.

Thread dell'utilità di pianificazione di messaggistica unificata

Un thread dell'utilità di pianificazione UMS è un thread normale che si è convertito in UMS chiamando la funzione EnterUmsSchedulingMode . L'utilità di pianificazione di sistema determina quando viene eseguito il thread dell'utilità di pianificazione UMS in base alla priorità rispetto ad altri thread pronti. Il processore in cui viene eseguito il thread dell'utilità di pianificazione è influenzato dall'affinità del thread, come per i thread non UMS.

Il chiamante di EnterUmsSchedulingMode specifica un elenco di completamento e una funzione del punto di ingresso UmsSchedulerProc da associare al thread dell'utilità di pianificazione UMS. Il sistema chiama la funzione del punto di ingresso specificata al termine della conversione del thread chiamante in UMS. La funzione del punto di ingresso dell'utilità di pianificazione è responsabile della determinazione dell'azione successiva appropriata per il thread specificato. Per altre informazioni, vedere Funzione punto di ingresso dell'utilità di pianificazione di messaggistica unificata più avanti in questo argomento.

Un'applicazione potrebbe creare un thread dell'utilità di pianificazione di messaggistica unificata per ogni processore che verrà usato per eseguire thread UMS. L'applicazione potrebbe anche impostare l'affinità di ogni thread dell'utilità di pianificazione UMS per un processore logico specifico, che tende a escludere thread non correlati dall'esecuzione in tale processore, riservandolo in modo efficace per tale thread dell'utilità di pianificazione. Tenere presente che l'impostazione dell'affinità dei thread in questo modo può influire sulle prestazioni complessive del sistema riducendo di fame altri processi che potrebbero essere in esecuzione nel sistema. Per altre informazioni sull'affinità di thread, vedere Più processori.

Thread di lavoro di messaggistica unificata, contesti di thread ed elenchi di completamento

Viene creato un thread di lavoro UMS chiamando CreateRemoteThreadEx con l'attributo PROC_THREAD_ATTRIBUTE_UMS_THREAD e specificando un contesto di thread UMS e un elenco di completamento.

Un contesto di thread UMS rappresenta lo stato del thread UMS di un thread di lavoro e viene usato per identificare il thread di lavoro nelle chiamate di funzione di messaggistica unificata. Viene creato chiamando CreateUmsThreadContext.

Viene creato un elenco di completamento chiamando la funzione CreateUmsCompletionList . Un elenco di completamento riceve thread di lavoro UMS che hanno completato l'esecuzione nel kernel e sono pronti per l'esecuzione in modalità utente. Solo il sistema può accodare thread di lavoro a un elenco di completamento. I nuovi thread di lavoro di messaggistica unificata vengono accodati automaticamente all'elenco di completamento specificato al momento della creazione dei thread. Anche i thread di lavoro bloccati in precedenza vengono accodati all'elenco di completamento quando non sono più bloccati.

Ogni thread dell'utilità di pianificazione di messaggistica unificata è associato a un singolo elenco di completamento. Tuttavia, lo stesso elenco di completamento può essere associato a un numero qualsiasi di thread dell'utilità di pianificazione di messaggistica unificata e un thread dell'utilità di pianificazione può recuperare i contesti di messaggistica unificata da qualsiasi elenco di completamento per il quale dispone di un puntatore.

Ogni elenco di completamento ha un evento associato segnalato dal sistema quando accoda uno o più thread di lavoro a un elenco vuoto. La funzione GetUmsCompletionListEvent recupera un handle per l'evento per un elenco di completamento specificato. Un'applicazione può attendere più di un evento elenco di completamento insieme ad altri eventi che hanno senso per l'applicazione.

Funzione punto di ingresso dell'utilità di pianificazione UMS

La funzione del punto di ingresso dell'utilità di pianificazione di un'applicazione viene implementata come funzione UmsSchedulerProc . Il sistema chiama la funzione del punto di ingresso dell'utilità di pianificazione dell'applicazione nei seguenti orari:

  • Quando un thread non UMS viene convertito in un thread dell'utilità di pianificazione UMS chiamando EnterUmsSchedulingMode.
  • Quando un thread di lavoro UMS chiama UmsThreadYield.
  • Quando un thread di lavoro di messaggistica unificata si blocca in un servizio di sistema, ad esempio una chiamata di sistema o un errore di pagina.

Il parametro Reason della funzione UmsSchedulerProc specifica il motivo per cui è stata chiamata la funzione del punto di ingresso. Se la funzione del punto di ingresso è stata chiamata perché è stato creato un nuovo thread dell'utilità di pianificazione UMS, il parametro SchedulerParam contiene i dati specificati dal chiamante di EnterUmsSchedulingMode. Se la funzione del punto di ingresso è stata chiamata perché è stato restituito un thread di lavoro UMS, il parametro SchedulerParam contiene i dati specificati dal chiamante di UmsThreadYield. Se la funzione del punto di ingresso è stata chiamata perché un thread di lavoro UMS bloccato nel kernel, il parametro SchedulerParam è NULL.

La funzione del punto di ingresso dell'utilità di pianificazione è responsabile della determinazione dell'azione successiva appropriata per il thread specificato. Ad esempio, se un thread di lavoro è bloccato, la funzione del punto di ingresso dell'utilità di pianificazione potrebbe eseguire il successivo thread di lavoro di messaggistica unificata pronto disponibile.

Quando viene chiamata la funzione del punto di ingresso dell'utilità di pianificazione, l'utilità di pianificazione dell'applicazione deve tentare di recuperare tutti gli elementi nell'elenco di completamento associato chiamando la funzione DequeueUmsCompletionListItems . Questa funzione recupera un elenco di contesti di thread UMS che hanno terminato l'elaborazione nel kernel e sono pronti per l'esecuzione in modalità utente. L'utilità di pianificazione dell'applicazione non deve eseguire thread UMS direttamente da questo elenco perché ciò può causare un comportamento imprevedibile nell'applicazione. L'utilità di pianificazione deve invece recuperare tutti i contesti del thread UMS chiamando la funzione GetNextUmsListItem una volta per ogni contesto, inserire i contesti del thread UMS nella coda del thread pronto dell'utilità di pianificazione e quindi eseguire solo i thread di messaggistica unificata dalla coda del thread pronto.

Se l'utilità di pianificazione non deve attendere più eventi, deve chiamare DequeueUmsCompletionListItems con un parametro di timeout diverso da zero, in modo che la funzione attenda l'evento dell'elenco di completamento prima di restituire. Se l'utilità di pianificazione deve attendere più eventi dell'elenco di completamento, deve chiamare DequeueUmsCompletionListItems con un parametro di timeout pari a zero in modo che la funzione restituisca immediatamente, anche se l'elenco di completamento è vuoto. In questo caso, l'utilità di pianificazione può attendere in modo esplicito sugli eventi dell'elenco di completamento, ad esempio usando WaitForMultipleObjects.

Esecuzione di thread di messaggistica unificata

Un thread di lavoro UMS appena creato viene accodato all'elenco di completamento specificato e non inizia l'esecuzione fino a quando l'utilità di pianificazione MESSAGGISTICA unificata dell'applicazione lo seleziona per l'esecuzione. Ciò è diverso dai thread non UMS, che l'utilità di pianificazione di sistema pianifica automaticamente l'esecuzione, a meno che il chiamante non crei in modo esplicito il thread sospeso.

L'utilità di pianificazione esegue un thread di lavoro chiamando ExecuteUmsThread con il contesto UMS del thread di lavoro. Un thread di lavoro UMS viene eseguito fino a quando non restituisce chiamando la funzione UmsThreadYield , i blocchi o termina.

Procedure consigliate per la messaggistica unificata

Le applicazioni che implementano MESSAGGISTICA unificata devono seguire queste procedure consigliate:

  • Le strutture sottostanti per i contesti di thread di messaggistica unificata vengono gestite dal sistema e non devono essere modificate direttamente. Usare invece QueryUmsThreadInformation e SetUmsThreadInformation per recuperare e impostare informazioni su un thread di lavoro di messaggistica unificata.
  • Per evitare deadlock, il thread dell'utilità di pianificazione UMS non deve condividere blocchi con thread di lavoro UMS. Sono inclusi sia i blocchi creati dall'applicazione che i blocchi di sistema acquisiti indirettamente da operazioni come l'allocazione dall'heap o il caricamento di DLL. Si supponga, ad esempio, che l'utilità di pianificazione esegua un thread di lavoro UMS che carica una DLL. Il thread di lavoro acquisisce il blocco e i blocchi del caricatore. Il sistema chiama la funzione del punto di ingresso dell'utilità di pianificazione, che quindi carica una DLL. Ciò causa un deadlock, perché il blocco del caricatore è già mantenuto e non può essere rilasciato fino a quando il primo thread non viene sbloccato. Per evitare questo problema, delegare il lavoro che potrebbe condividere blocchi con thread di lavoro di messaggistica unificata a un thread di lavoro UMS dedicato o a un thread non UMS.
  • UMS è più efficiente quando la maggior parte dell'elaborazione viene eseguita in modalità utente. Quando possibile, evitare di effettuare chiamate di sistema nei thread di lavoro di messaggistica unificata.
  • I thread di lavoro UMS non devono presupporre che venga usata l'utilità di pianificazione del sistema. Questo presupposto può avere effetti sottili; Ad esempio, se un thread nel codice sconosciuto imposta una priorità di thread o un'affinità, l'utilità di pianificazione MESSAGGISTICA unificata potrebbe comunque eseguirne l'override. Il codice che presuppone che l'utilità di pianificazione del sistema venga usata potrebbe non comportarsi come previsto e potrebbe interrompersi quando viene chiamato da un thread UMS.
  • Il sistema potrebbe dover bloccare il contesto del thread di un thread di lavoro di messaggistica unificata. Ad esempio, una chiamata di procedura asincrona in modalità kernel (APC) potrebbe modificare il contesto del thread UMS, quindi il contesto del thread deve essere bloccato. Se l'utilità di pianificazione tenta di eseguire il contesto del thread di messaggistica unificata mentre è bloccata, la chiamata avrà esito negativo. Questo comportamento è previsto dalla progettazione e l'utilità di pianificazione deve essere progettata per ritentare l'accesso al contesto del thread di messaggistica unificata.