Afficher en anglais

Partage via


DispatcherQueue

Points essentiels

  • La classe DispatcherQueue dans le Kit de développement logiciel (SDK) d’application Windows gère une file d’attente hiérarchisée sur laquelle les tâches d’un thread s’exécutent de manière série.
  • Il fournit un moyen pour les threads d’arrière-plan d’exécuter du code sur le thread d’un DispatcherQueue (par exemple, le thread d’interface utilisateur où les objets avec une affinité de thread sont actifs).
  • La classe s’intègre précisément aux boucles de message arbitraires. Par exemple, il prend en charge l’idiome Win32 commun des boucles de message imbriquées.
  • La classe AppWindow s’intègre à DispatcherQueue : lorsqu’un DispatcherQueue pour un thread donné est arrêté, les instances AppWindow sont automatiquement détruites.
  • Il fournit un moyen d’inscrire un délégué appelé lorsqu’un délai d’expiration expire.
  • Il fournit des événements qui indiquent aux composants lorsqu’une boucle de message se termine, et différer éventuellement cet arrêt jusqu’à ce que le travail en attente soit terminé. Cela garantit que les composants qui utilisent DispatcherQueue, mais qui ne possèdent pas la boucle de message, peuvent effectuer un nettoyage sur le thread lorsque la boucle se ferme.
  • DispatcherQueue est un singleton de thread (il peut y avoir au maximum l’un d’entre eux s’exécutant sur n’importe quel thread donné). Par défaut, un thread n’a pas de DispatcherQueue.
  • Un propriétaire de thread peut créer un DispatcherQueueController pour initialiser dispatcherQueue pour le thread. À ce stade, n’importe quel code peut accéder à dispatcherQueue du thread, mais seul le propriétaire de DispatcherQueueController a accès à la méthode DispatcherQueueController.ShutdownQueue, qui draine le DispatcherQueue et déclenche les événements ShutdownStarted et ShutdownCompleted.
  • Un propriétaire de boucle de message le plus externe doit créer une instance DispatcherQueue . Seul le code chargé d’exécuter la boucle de message la plus externe d’un thread sait quand la distribution est terminée, c’est-à-dire le moment approprié pour arrêter dispatcherQueue. Cela signifie que les composants qui s’appuient sur DispatcherQueue ne doivent pas créer le DispatcherQueue , sauf s’ils possèdent la boucle de message du thread.

Run-down

Une fois qu’un thread quitte sa boucle d’événements, il doit arrêter son DispatcherQueue. Cela déclenche les événements ShutdownStarting et ShutdownCompleted et vide tous les éléments mis en file d’attente finals avant de désactiver la mise en file d’attente supplémentaire.

  • Pour arrêter un DispatcherQueue qui s’exécute sur un thread dédié avec une boucle de message appartenant à DispatcherQueue, appelez la méthode DispatcherQueueController.ShutdownQueueAsync.
  • Pour les scénarios où l’application possède une boucle de message arbitraire (par exemple, XAML Islands), appelez la méthode Synchrone DispatcherQueueController.ShutdownQueueue . Cette méthode déclenche des événements d’arrêt et draine le DispatcherQueue de façon synchrone sur le thread appelant.

Lorsque vous appelez DispatcherQueueController.ShutdownQueueAsync ou DispatcherQueueController.ShutdownQueue, l’ordre des événements déclenchés est le suivant :

  • ShutdownStarting. Destiné aux applications à gérer.
  • FrameworkShutdownStarting. Destiné aux frameworks à gérer.
  • FrameworkShutdownCompleted. Destiné aux frameworks à gérer.
  • ShutdownCompleted. Destiné aux applications à gérer.

Les événements sont séparés en catégories d’application/framework afin que l’arrêt ordonné puisse être atteint. Autrement dit, en levant explicitement l’arrêt de l’application avant les événements d’arrêt du framework, il n’existe aucun danger qu’un composant de framework soit dans un état inutilisable lorsque l’application se décompose.

C++/WinRT
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();
}

Boucles de message les plus externes et récursives

DispatcherQueue prend en charge les boucles de message personnalisées. Toutefois, pour les applications simples qui n’ont pas besoin de personnalisation, nous fournissons des implémentations par défaut. Cela supprime une charge des développeurs et permet de garantir un comportement cohérent.

C++/WinRT
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();
}

Gestion du répartiteur système

Certains composants du Kit de développement logiciel (SDK) d’application Windows (par exemple, MicaController) dépendent des composants système qui nécessitent à leur tour un système DispatcherQueue (Windows.System.DispatcherQueue) s’exécutant sur le thread.

Dans ce cas, le composant doté d’une dépendance system DispatcherQueue appelle la méthode EnsureSystemDispatcherQueue , ce qui libère votre application de la gestion d’un système DispatcherQueue.

Avec cette méthode appelée, le kit de développement logiciel (SDK) d’application Windows DispatcherQueue gère automatiquement la durée de vie du système DispatcherQueue, en arrêtant le dispatcherQueue système en même temps que le répartiteur du SDK d’application Windows. Les composants peuvent s’appuyer sur les événements d’arrêt du kit SDK d’application Windows et du système DispatcherQueue afin de s’assurer qu’ils effectuent un nettoyage approprié après la sortie de la boucle de message.

C++/WinRT
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();
}

Intégration d’AppWindow

La classe AppWindow a des fonctionnalités qui l’intègrent à DispatcherQueue, afin que les objets AppWindow puissent être détruits automatiquement lorsque la méthode DispatcherQueueController.ShutdownQueueAsync ou DispatcherQueueController.ShutdownQueue.ShutdownQueue Est appelée.

Il existe également une propriété d’AppWindow qui permet aux appelants de récupérer le DispatcherQueue associé à AppWindow ; en l’alignant avec d’autres objets dans les espaces de noms Composition et Input.

AppWindow a besoin de votre opt-in explicite pour être conscient de dispatcherQueue.

C++/WinRT
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();
}