Condividi tramite


DispatcherQueue

Caratteristiche principali

  • La classe DispatcherQueue nell'SDK per app di Windows gestisce una coda prioritaria su cui i compiti di un thread vengono eseguiti in modo seriale.
  • Fornisce ai thread in background la possibilità di eseguire codice su un thread di DispatcherQueue (ad esempio, il thread dell'interfaccia utente in cui risiedono gli oggetti con affinità di thread).
  • La classe si integra perfettamente con i cicli di messaggi arbitrari. Ad esempio, supporta il comune idioma Win32 dei cicli di messaggi annidati.
  • La classe AppWindow si integra con DispatcherQueue; quando DispatcherQueue per un determinato thread viene chiusa, le istanze di AppWindow vengono automaticamente distrutte.
  • Fornisce un mezzo per registrare un delegato che viene chiamato quando scade il timeout.
  • Fornisce eventi che consentono ai componenti di sapere quando un ciclo di messaggi sta per uscire e, facoltativamente, di rinviare la chiusura fino al completamento del lavoro in sospeso. Questo assicura che i componenti che utilizzano DispatcherQueue, ma che non possiedono il ciclo di messaggi, possano effettuare la pulizia on-thread quando il ciclo esce.
  • DispatcherQueue è un thread singleton (può essercene al massimo uno in esecuzione su un determinato thread). Per impostazione predefinita, un thread non ha DispatcherQueue.
  • Il proprietario di un thread può creare un DispatcherQueueController per inizializzare il DispatcherQueue per il thread. A quel punto, qualsiasi codice può accedere a DispatcherQueue del thread; ma solo il proprietario di DispatcherQueueController ha accesso al metodo DispatcherQueController.ShutdownQueue che svuota DispatcherQueue e genera eventi ShutdownStarted e ShutdownCompleted.
  • Il proprietario di un ciclo di messaggi più esterno deve creare un'istanza DispatcherQueue. Solo il codice incaricato di eseguire il ciclo di messaggi più esterno di un thread sa quando l'invio è completato e quando è il momento giusto per chiudere DispatcherQueue. Ciò significa che i componenti che si affidano a DispatcherQueue non devono creare DispatcherQueue a meno che non siano proprietari del ciclo di messaggi del thread.

Riepilogo

Dopo che un thread esce dal suo ciclo di eventi, deve chiudere la relativa DispatcherQueue. In questo modo si sollevano gli eventi ShutdownStarting e ShutdownCompleted e si svuotano tutti gli ultimi elementi in attesa prima di disabilitare l'ulteriore inserimento in lista.

  • Per chiudere un thread DispatcherQueue dedicato con un ciclo di messaggi DispatcherQueue di proprietà, chiama il metodo DispatcherQueueController.ShutdownQueueAsync.
  • Per gli scenari in cui l'applicazione possiede un ciclo di messaggi arbitrario (ad esempio le isole XAML), chiama il metodo sincrono DispatcherQueueController.ShutdownQueue. Questo metodo solleva eventi di spegnimento e svuota la DispatcherQueue in modo sincrono sul thread chiamante.

Quando si chiama DispatcherQueueController.ShutdownQueueAsync o DispatcherQueueController.ShutdownQueue, l'ordine degli eventi è il seguente:

  • ShutdownStarting. Destinato alla gestione da parte delle app.
  • FrameworkShutdownStarting. Destinato alla gestione dei framework.
  • FrameworkShutdownCompleted. Destinato alla gestione dei framework.
  • ShutdownCompleted. Destinato alla gestione da parte delle app.

Gli eventi sono separati in categorie di applicazioni/framework in modo da poter ottenere una chiusura ordinata. In altre parole, sollevando esplicitamente lo spegnimento dell'applicazione prima degli eventi di spegnimento del framework, non c'è il rischio che un componente del framework si trovi in uno stato inutilizzabile quando l'applicazione si esaurisce.

namespace winrt 
{
    using namespace Microsoft::UI::Dispatching;
}

// App runs its own custom message loop.
void RunCustomMessageLoop()
{
    // Create a DispatcherQueue.
    auto dispatcherQueueController{winrt::DispatcherQueueController::CreateOnCurrentThread()};

    // Run a custom message loop. Runs until the message loop owner decides to stop.
    MSG msg;
    while (GetMessage(&msg, nullptr, 0, 0))
    {
        if (!ContentPreTranslateMessage(&msg))
        {
            TranslateMesasge(&msg);
            DispatchMessage(&msg);
        }
    }

    // Run down the DispatcherQueue. This single call also runs down the system DispatcherQueue
    // if one was created via EnsureSystemDispatcherQueue:
    // 1. Raises DispatcherQueue.ShutdownStarting event.
    // 2. Drains remaining items in the DispatcherQueue, waits for deferrals.
    // 3. Raises DispatcherQueue.FrameworkShutdownStarting event.
    // 4. Drains remaining items in the DispatcherQueue, waits for deferrals.
    // 5. Disables further enqueuing.
    // 6. Raises the DispatcherQueue.FrameworkShutdownCompleted event.
    // 7. Raises the DispatcherQueue.ShutdownCompleted event.    

    dispatcherQueueController.ShutdownQueue();
}

Ciclo di messaggi più esterno e ricorsivo

DispatcherQueue supporta i cicli di messaggi personalizzati. Tuttavia, per le applicazioni semplici che non necessitano di personalizzazioni, forniamo un'implementazione predefinita. Questo fa risparmiare tempo agli sviluppatori e aiuta a garantire un comportamento sempre corretto.

namespace winrt 
{
    using namespace Microsoft::UI::Dispatching;
}

// Simple app; doesn't need a custom message loop.
void RunMessageLoop()
{
    // Create a DispatcherQueue.
    auto dispatcherQueueController{winrt::DispatcherQueueController::CreateOnCurrentThread()};

    // Runs a message loop until a call to DispatcherQueue.EnqueueEventLoopExit or PostQuitMessage.
    dispatcherQueueController.DispatcherQueue().RunEventLoop();

    // Run down the DispatcherQueue. 
    dispatcherQueueController.ShutdownQueue();
}

// May be called while receiving a message.
void RunNestedLoop(winrt::DispatcherQueue dispatcherQueue)
{
    // Runs a message loop until a call to DispatcherQueue.EnqueueEventLoopExit or PostQuitMessage.
    dispatcherQueue.RunEventLoop();
}

// Called to break out of the message loop, returning from the RunEventLoop call lower down the
// stack.
void EndMessageLoop(winrt::DispatcherQueue dispatcherQueue)
{
    // Alternatively, calling Win32's PostQuitMessage has the same effect.
    dispatcherQueue.EnqueueEventLoopExit();
}

Gestione del dispatcher di sistema

Alcuni componenti del Windows App SDK (ad esempio, MicaController) dipendono da componenti di sistema che a loro volta richiedono una DispatcherQueue (Windows.System.DispatcherQueue) in esecuzione sul thread.

In questi casi, il componente che ha una dipendenza DispatcherQueue di sistema chiama il metodo EnsureSystemDispatcherQueue , liberando l'applicazione dalla gestione di una DispatcherQueue di sistema.

Una volta richiamato questo metodo, la DispatcherQueue dell'SDK per l'app Windows gestisce la durata della DispatcherQueue di sistema automaticamente, chiudendo la DispatcherQueue del sistema insieme alla DispatcherQueue dell'SDK per l'app Windows. I componenti potrebbero fare affidamento sugli eventi di spegnimento della DispatcherQueue dell'SDK per app di Windows e di sistema per assicurarsi di eseguire una pulizia adeguata dopo l'uscita del ciclo di messaggi.

namespace winrt 
{
    using namespace Microsoft::UI::Composition::SystemBackdrops;
    using namespace Microsoft::UI::Dispatching;
}

// The Windows App SDK component calls this during its startup.
void MicaControllerInitialize(winrt::DispatcherQueue dispatcherQueue)
{
    dispatcherQueue.EnsureSystemDispatcherQueue();

    // If the component needs the system DispatcherQueue explicitly, it can now grab it off the thread.
    winrt::Windows::System::DispatcherQueue systemDispatcherQueue =
        winrt::Windows::System::DispatcherQueue::GetForCurrentThread();
}

void AppInitialize()
{
    // App doesn't need to concern itself with the system DispatcherQueue dependency.
    auto micaController = winrt::MicaController();
}

Integrazione di AppWindow

La classe AppWindow include funzionalità che la integrano con la DispatcherQueue, in modo che gli oggetti AppWindow possano essere eliminati automaticamente quando viene chiamato il metodo DispatcherQueueController.ShutdownQueueAsync o DispatcherQueueController.ShutdownQueue.

Esiste anche una proprietà di AppWindow che consente ai chiamanti di recuperare la DispatcherQueue associata ad AppWindow, allineandolo ad altri oggetti negli spazi dei nomi Composition e Input.

AppWindow richiede il consenso esplicito per essere a conoscenza di DispatcherQueue.

namespace winrt 
{
    using namespace Microsoft::UI::Dispatching;
    using namespace Microsoft::UI::Windowing;
}

void Main()
{
    // Create a Windows App SDK DispatcherQueue.
    auto dispatcherQueueController{winrt::DispatcherQueueController::CreateOnCurrentThread()};

    var appWindow = AppWindow.Create(nullptr, 0, dispatcherQueueController.DispatcherQueue());

    // Since we associated the DispatcherQueue above with the AppWindow, we're able to retreive it 
    // as a property. If we were to not associate a dispatcher, this property would be null.
    ASSERT(appWindow.DispatcherQueue() == dispatcherQueueController.DispatcherQueue());

    // Runs a message loop until a call to DispatcherQueue.EnqueueEventLoopExit or PostQuitMessage.
    dispatcherQueueController.DispatcherQueue().RunEventLoop();

    // Rundown the Windows App SDK DispatcherQueue. While this call is in progress, the AppWindow.Destoyed
    // event will be raised since the AppWindow instance is associated with the DispatcherQueue.
    dispatcherQueueController.ShutdownQueue();
}