DispatcherQueue

Elementi di rilievo

  • La classe DispatcherQueue nella SDK per app di Windows gestisce una coda con priorità in cui le attività per un thread vengono eseguite in modo seriale.
  • Fornisce un mezzo per consentire ai thread in background di eseguire il codice nel thread di DispatcherQueue, ad esempio il thread dell'interfaccia utente in cui sono attivi gli oggetti con affinità di thread.
  • La classe si integra con precisione con cicli di messaggi arbitrari. Ad esempio, supporta il linguaggio Win32 comune dei cicli di messaggi annidati.
  • La classe AppWindow si integra con DispatcherQueue: quando un DispatcherQueue per un determinato thread viene arrestato, le istanze di AppWindow vengono eliminate automaticamente.
  • Fornisce un mezzo per registrare un delegato chiamato alla scadenza di un timeout.
  • Fornisce eventi che notificano ai componenti quando il ciclo di messaggi sta per terminare e, facoltativamente, consentono di posticipare tale arresto fino al completamento delle operazioni in sospeso. In questo modo, i componenti che usano DispatcherQueue, ma non sono proprietari del ciclo di messaggi, possono eseguire operazioni di pulizia sul thread all'uscita del ciclo.
  • DispatcherQueue è un singleton del thread (può essere presente al massimo uno di essi in esecuzione in qualsiasi thread specificato). Per impostazione predefinita, un thread non ha DispatcherQueue.
  • Un proprietario del thread può creare un DispatcherQueueController per inizializzare DispatcherQueue per il thread. A questo punto, qualsiasi codice può accedere al DispatcherQueue del thread; ma solo il proprietario di DispatcherQueueController ha accesso al metodo DispatcherQueueController.ShutdownQueue , che svuota DispatcherQueue e genera gli eventi ShutdownStarting e ShutdownCompleted .
  • Un proprietario del ciclo di messaggi più esterno deve creare un'istanza dispatcherQueue . Solo il codice responsabile dell'esecuzione del ciclo di messaggi più esterno di un thread sa quando l'invio è completo, ovvero il tempo appropriato per arrestare DispatcherQueue. Ciò significa che i componenti che si basano su DispatcherQueue non devono creare DispatcherQueue a meno che non siano proprietari del ciclo di messaggi del thread.

Malandato

Dopo che un thread ha chiuso il ciclo di eventi, deve arrestare il dispatcherQueue. In questo modo vengono generati gli eventi ShutdownStarting e ShutdownCompleted e vengono svuotati gli elementi accodati in sospeso finali prima di disabilitare ulteriormente l'accodamento.

  • Per arrestare un DispatcherQueue in esecuzione in un thread dedicato con un ciclo di messaggi gestito da DispatcherQueue, chiama il metodo DispatcherQueueController.ShutdownQueueAsync.
  • Per gli scenari in cui l'app gestisce un loop di messaggi personalizzato (ad esempio, XAML Islands), chiamare il metodo sincrono DispatcherQueueController.ShutdownQueue. Questo metodo genera eventi di arresto e svuota il dispatcherQueue in modo sincrono sul thread chiamante.

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

  • ShutdownStarting. Destinato a essere gestito dalle app.
  • FrameworkShutdownStarting. Pensato per essere gestito dai framework.
  • FrameworkShutdownCompleted. Destinato a essere gestito dai framework.
  • ShutdownCompleted. Destinato a essere gestito dalle app.

Gli eventi sono suddivisi in categorie di applicazione/framework, così da consentire un arresto controllato. Ciò significa che, generando in modo esplicito l'arresto dell'applicazione prima degli eventi di arresto del framework, non c'è pericolo che un componente del framework sia in uno stato inutilizzabile quando l'applicazione si arresta.

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))
        {
            TranslateMessage(&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();
}

Cicli di messaggi più esterni e ricorsivi

DispatcherQueue supporta cicli di messaggi personalizzati. Tuttavia, per le app semplici che non richiedono la personalizzazione, vengono fornite implementazioni predefinite. Ciò comporta la rimozione di un carico da parte degli sviluppatori e garantisce un comportamento coerentemente 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 SDK per app di Windows (ad esempio, MicaController) dipendono dai componenti di sistema che a loro volta richiedono un sistema DispatcherQueue (Windows. System.DispatcherQueue) in esecuzione nel thread.

In questi casi, il componente con una dipendenza DispatcherQueue di sistema chiama il metodo EnsureSystemDispatcherQueue , liberando l'app dalla gestione di un dispatcherQueue di sistema.

Una volta chiamato questo metodo, il DispatcherQueue di SDK per app di Windows gestisce automaticamente il ciclo di vita del DispatcherQueue di sistema, arrestando il DispatcherQueue di sistema insieme al DispatcherQueue di SDK per app di Windows. I componenti possono basarsi sia sugli eventi di arresto di SDK per app di Windows sia su quelli del sistema DispatcherQueue per garantire che le operazioni di pulizia vengano eseguite correttamente dopo la terminazione 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 DispatcherQueue, in modo che gli oggetti AppWindow possano essere eliminati automaticamente quando viene chiamato il metodo DispatcherQueueController.ShutdownQueueAsync o DispatcherQueueController.ShutdownQueue.

C'è anche una proprietà di AppWindow che consente ai chiamanti di recuperare il DispatcherQueue associato ad AppWindow; allineandolo con 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()};

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

    // Since we associated the DispatcherQueue above with the AppWindow, we're able to retrieve 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();
}