Compartilhar via


DispatcherQueue

Destaques

  • A classe DispatcherQueue no SDK do Aplicativo Windows gerencia uma fila priorizada na qual as tarefas de um thread são executadas de forma serial.
  • Ele fornece um meio para que threads em segundo plano executem código no thread de um DispatcherQueue (por exemplo, o thread da interface do usuário em que os objetos com afinidade de thread vivem).
  • A classe se integra precisamente a loops de mensagens arbitrárias. Por exemplo, ele dá suporte ao idioma comum do Win32 de loops de mensagens aninhadas.
  • A classe AppWindow se integra ao DispatcherQueue — quando um DispatcherQueue para um determinado thread está sendo desligado, as instâncias do AppWindow são destruídas automaticamente.
  • Ele fornece um meio de registrar um delegado que é chamado quando um tempo limite expira.
  • Ele fornece eventos que permitem que os componentes saibam quando um loop de mensagem está saindo e, opcionalmente, adiam esse desligamento até que o trabalho pendente seja concluído. Isso garante que os componentes que usam o DispatcherQueue, mas não possuem o loop de mensagem, possam fazer a limpeza no thread à medida que o loop é encerrado.
  • O DispatcherQueue é um singleton de thread (pode haver no máximo um deles em execução em qualquer thread). Por padrão, um thread não tem DispatcherQueue.
  • Um proprietário de thread pode criar um DispatcherQueueController para inicializar o DispatcherQueue para o thread. Nesse ponto, qualquer código pode acessar o DispatcherQueue do thread; mas apenas o proprietário do DispatcherQueueController tem acesso ao método DispatcherQueueController.ShutdownQueue, que drena o DispatcherQueue e gera os eventos ShutdownStarted e ShutdownCompleted.
  • Um proprietário de loop de mensagem mais externo deve criar uma instância DispatcherQueue . Somente o código encarregado de executar o loop de mensagem mais externo de um thread sabe quando a expedição é concluída, que é o momento apropriado para desligar o DispatcherQueue. Isso significa que os componentes que dependem de DispatcherQueue não devem criar o DispatcherQueue, a menos que possuam o loop de mensagem do thread.

Esgotamento

Depois que um thread sai de seu loop de eventos, ele deve desligar seu DispatcherQueue. Isso gera os eventos ShutdownStarting e ShutdownCompleted e drena todos os itens enfileirados pendentes finais antes de desabilitar mais enfileiramentos.

  • Para desligar um DispatcherQueue que está em execução em um thread dedicado com um loop de mensagem de propriedade do DispatcherQueue, chame o método DispatcherQueueController.ShutdownQueueAsync.
  • Para cenários em que o aplicativo possui um loop de mensagem arbitrário (por exemplo, Ilhas XAML), chame o método DispatcherQueueController.ShutdownQueue síncrono. Esse método gera eventos de desligamento e drena o DispatcherQueue de forma síncrona no thread de chamada.

Quando você chama DispatcherQueueController.ShutdownQueueAsync ou DispatcherQueueController.ShutdownQueue, a ordem dos eventos gerados é a seguinte:

  • DesligamentoIniciando. Destinado a aplicativos para manipular.
  • FrameworkShutdownStarting. Destinado a estruturas para manipular.
  • FrameworkShutdownCompleted. Destinado a estruturas para manipular.
  • ShutdownCompleted. Destinado a aplicativos para manipular.

Os eventos são separados em categorias de aplicativo/estrutura para que o desligamento ordenado possa ser alcançado. Ou seja, ao aumentar explicitamente o desligamento do aplicativo antes dos eventos de desligamento da estrutura, não há perigo de que um componente da estrutura fique em um estado inutilizável à medida que o aplicativo é encerrado.

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

Loops de mensagens mais externos e recursivos

DispatcherQueue dá suporte a loops de mensagens personalizados. No entanto, para aplicativos simples que não precisam de personalização, fornecemos implementações padrão. Isso remove um fardo dos desenvolvedores e ajuda a garantir um comportamento consistentemente correto.

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

Gerenciamento do despachante do sistema

Alguns componentes SDK do Aplicativo Windows (por exemplo, MicaController) dependem de componentes do sistema que, por sua vez, exigem um DispatcherQueue do sistema (Windows.System.DispatcherQueue) em execução no thread.

Nesses casos, o componente que tem uma dependência DispatcherQueue do sistema chama o método EnsureSystemDispatcherQueue, liberando seu aplicativo do gerenciamento de um DispatcherQueue do sistema.

Com esse método chamado, o SDK do Aplicativo Windows DispatcherQueue gerencia o tempo de vida do DispatcherQueue do sistema automaticamente, desligando o DispatcherQueue do sistema junto com o SDK do Aplicativo Windows DispatcherQueue. Os componentes podem depender de SDK do Aplicativo Windows e eventos de desligamento DispatcherQueue do sistema para garantir que eles façam a limpeza adequada após a saída do loop de mensagem.

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

Integração AppWindow

A classe AppWindow tem uma funcionalidade que a integra ao DispatcherQueue, para que os objetos AppWindow possam ser destruídos automaticamente quando o método DispatcherQueueController.ShutdownQueueAsync ou DispatcherQueueController.ShutdownQueue for chamado.

Há também uma propriedade de AppWindow que permite que os chamadores recuperem o DispatcherQueue associado ao AppWindow; alinhando-o com outros objetos nos namespaces Composition e Input .

AppWindow precisa de sua aceitação explícita para estar ciente do 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();
}