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 os threads em segundo plano executarem código no thread de um DispatcherQueue (por exemplo, o thread de interface do usuário em que os objetos com afinidade de thread residem).
  • A classe integra-se precisamente com loops de mensagens arbitrárias. Por exemplo, ele dá suporte ao idioma Win32 comum de loops de mensagens aninhadas.
  • A classe AppWindow integra-se ao DispatcherQueue— quando um DispatcherQueue para um determinado thread está sendo desligado, as instâncias do AppWindow são automaticamente destruídas.
  • Ele fornece um meio de registrar um delegado chamado quando um tempo limite expira.
  • Ele fornece eventos que permitem aos componentes saber quando um loop de mensagem está saindo e, opcionalmente, adiar 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 conforme o loop é encerrado.
  • O DispatcherQueue é um singleton de thread (pode haver no máximo um deles em execução em qualquer thread específico). 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 somente o proprietário do DispatcherQueueController tem acesso ao método DispatcherQueueController.ShutdownQueue , que drena o DispatcherQueue e gera eventos ShutdownStarting e ShutdownCompleted .
  • O proprietário do loop de mensagens mais externo deve criar uma instância de DispatcherQueue. Somente o código encarregado de executar o loop de mensagens mais externo de uma thread sabe quando o processamento é concluído, e esse é o momento apropriado para encerrar a DispatcherQueue. Isso significa que os componentes que dependem do DispatcherQueue não devem criar o DispatcherQueue , a menos que possuam o loop de mensagem do thread.

Resumo

Depois que um thread sair do loop de eventos, ele deverá desligar o DispatcherQueue. Isso gera os eventos ShutdownStarting e ShutdownCompleted e drena todos os itens enfileirados pendentes finais antes de desabilitar a enfileiramento adicional.

  • Para desligar uma DispatcherQueue em execução em uma thread dedicada com um loop de mensagens de propriedade da DispatcherQueue, chame o método DispatcherQueueController.ShutdownQueueAsync.
  • Nos cenários em que o aplicativo controla um loop de mensagens arbitrário (por exemplo, XAML Islands), chame o método síncrono DispatcherQueueController.ShutdownQueue. Esse método dispara eventos de desligamento e esvazia a DispatcherQueue de forma síncrona na thread de chamada.

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

  • ShutdownStarting. Destinado ao processamento por aplicativos.
  • FrameworkShutdownStarting. Destinado para frameworks lidarem com isso.
  • FrameworkShutdownCompleted. Destinado para frameworks lidarem com isso.
  • ShutdownCompleted. Destinado ao processamento por aplicativos.

Os eventos são separados em categorias de aplicativo/estrutura para que o desligamento ordenado possa ser alcançado. Ou seja, ao antecipar explicitamente o encerramento do aplicativo em relação aos eventos de desligamento do framework, não há risco de que um componente do framework fique em um estado não utilizável durante o encerramento do aplicativo.

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

Loops de mensagem mais externos e recursivos

DispatcherQueue dá suporte a loops de mensagem personalizados. No entanto, para aplicativos simples que não precisam de personalização, fornecemos uma implementação padrão. Isso remove uma carga 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 sistema DispatcherQueue (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 de gerenciar um DispatcherQueue do sistema.

Com esse método chamado, o SDK do Aplicativo Windows DispatcherQueue gerencia o tempo de vida do sistema DispatcherQueue automaticamente, desligando o sistema DispatcherQueue ao lado do SDK do Aplicativo Windows DispatcherQueue. Os componentes podem depender tanto dos eventos de desligamento do SDK do Aplicativo Windows quanto dos eventos de desligamento do sistema de DispatcherQueue para garantir que realizem a limpeza adequada após o encerramento do loop de mensagens.

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 do AppWindow

A classe AppWindow inclui funcionalidade que a integra à DispatcherQueue, de forma que os objetos AppWindow possam ser destruídos automaticamente quando os métodos DispatcherQueueController.ShutdownQueueAsync ou DispatcherQueueController.ShutdownQueue forem chamados.

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 de Composição e Entrada .

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

    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.Destroyed
    // event will be raised since the AppWindow instance is associated with the DispatcherQueue.
    dispatcherQueueController.ShutdownQueue();
}

Consulte também