Compartir a través de


DispatcherQueue

Aspectos destacados

  • La clase DispatcherQueue del SDK de Aplicaciones para Windows administra una cola con prioridad en la que las tareas de un subproceso se ejecutan de forma serie.
  • Proporciona un medio para que los subprocesos en segundo plano ejecuten código en un subproceso de DispatcherQueue (por ejemplo, el subproceso de la interfaz de usuario en el que los objetos con afinidad de subproceso están activos).
  • La clase se integra precisamente con bucles de mensajes arbitrarios. Por ejemplo, admite la expresión win32 común de bucles de mensajes anidados.
  • La clase AppWindow se integra con DispatcherQueue, cuando se apaga un dispatcherQueue para un subproceso determinado, las instancias de AppWindow se destruyen automáticamente.
  • Proporciona un medio para registrar un delegado al que se llama cuando expira un tiempo de espera.
  • Proporciona eventos que permiten a los componentes saber cuándo sale un bucle de mensajes y, opcionalmente, aplazar ese apagado hasta que se complete el trabajo pendiente. Esto garantiza que los componentes que usan DispatcherQueue, pero que no poseen el bucle de mensajes, pueden realizar la limpieza en el subproceso a medida que se cierra el bucle.
  • DispatcherQueue es un singleton de subproceso (puede haber como máximo uno de ellos en ejecución en cualquier subproceso determinado). De forma predeterminada, un subproceso no tiene DispatcherQueue.
  • Un propietario del subproceso puede crear un DispatcherQueueController para inicializar dispatcherQueue para el subproceso. En ese momento, cualquier código puede tener acceso al dispatcherQueue del subproceso, pero solo el propietario de DispatcherQueueController tiene acceso al método DispatcherQueueController.ShutdownQueue, que purga dispatcherQueue y genera eventos ShutdownStarted y ShutdownCompleted.
  • Un propietario de bucle de mensajes externo debe crear una instancia de DispatcherQueue . Solo el código encargado de ejecutar el bucle de mensajes más externo de un subproceso sabe cuándo se ha completado el envío, que es el momento adecuado para apagar DispatcherQueue. Esto significa que los componentes que dependen de DispatcherQueue no deben crear dispatcherQueue a menos que posean el bucle de mensajes del subproceso.

Degradación

Después de que un subproceso salga de su bucle de eventos, debe apagar su DispatcherQueue. Al hacerlo, se generan los eventos ShutdownStarting y ShutdownCompleted y se purgan los elementos en cola pendientes finales antes de deshabilitar la puesta en cola.

  • Para apagar un dispatcherQueue que se ejecuta en un subproceso dedicado con un bucle de mensajes de propiedad de DispatcherQueue, llame al método DispatcherQueueController.ShutdownQueueAsync .
  • En escenarios en los que la aplicación posee un bucle de mensajes arbitrario (por ejemplo, islas XAML), llame al método DispatcherQueueController.ShutdownQueue sincrónico. Ese método genera eventos de apagado y purga el dispatcherQueue sincrónicamente en el subproceso que realiza la llamada.

Al llamar a DispatcherQueueController.ShutdownQueueAsync o DispatcherQueueController.ShutdownQueue, el orden de los eventos generados es el siguiente:

  • ShutdownStarting. Diseñado para que las aplicaciones se controle.
  • FrameworkShutdownStarting. Diseñado para los marcos de trabajo que se van a controlar.
  • FrameworkShutdownCompleted. Diseñado para los marcos de trabajo que se van a controlar.
  • ShutdownCompleted. Diseñado para que las aplicaciones se controle.

Los eventos se separan en categorías de aplicación o marco para que se pueda lograr el apagado ordenado. Es decir, al generar explícitamente el apagado de la aplicación antes de los eventos de cierre del marco, no hay ningún peligro de que un componente de marco esté en un estado inutilizable a medida que la aplicación deja de funcionar.

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

Bucles de mensajes más externos y recursivos

DispatcherQueue admite bucles de mensajes personalizados. Sin embargo, para aplicaciones sencillas que no necesitan personalización, proporcionamos implementaciones predeterminadas. Esto elimina una carga de los desarrolladores y ayuda a garantizar un comportamiento coherentemente correcto.

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

Administración del distribuidor del sistema

Algunos componentes de SDK de Aplicaciones para Windows (por ejemplo, MicaController) dependen de los componentes del sistema que, a su vez, requieren un sistema DispatcherQueue (Windows.System.DispatcherQueue) que se ejecuta en el subproceso.

En esos casos, el componente que tiene una dependencia dispatcherQueue del sistema llama al método EnsureSystemDispatcherQueue, lo que libera la aplicación de administrar un sistema DispatcherQueue.

Con ese método llamado, el SDK de Aplicaciones para Windows DispatcherQueue administra automáticamente la duración del sistema DispatcherQueue, cerrando el sistema DispatcherQueue junto con el SDK de Aplicaciones para Windows DispatcherQueue. Los componentes pueden depender de eventos de apagado de SDK de Aplicaciones para Windows y dispatcherQueue del sistema para asegurarse de que realizan una limpieza adecuada después de que se cierre el bucle de mensajes.

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

Integración de AppWindow

La clase AppWindow tiene funcionalidad que la integra con dispatcherQueue, de modo que los objetos AppWindow se puedan destruir automáticamente cuando se llama al método DispatcherQueueController.ShutdownQueueAsync o DispatcherQueueController.ShutdownQueue.

También hay una propiedad de AppWindow que permite a los autores de llamadas recuperar el DispatcherQueue asociado a AppWindow; alinearlo con otros objetos de los espacios de nombres Composition y Input.

AppWindow necesita su participación explícita para tener en cuenta 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();
}