DispatcherQueue

亮点

  • Windows 应用 SDK中的 DispatcherQueue 类管理线程的任务以串行方式执行的优先级队列。
  • 它为后台线程提供了一种在 DispatcherQueue 线程上运行代码的方法(例如,具有线程相关性的对象所在的 UI 线程)。
  • 该类可与任意消息循环精确配合。 例如,它支持嵌套消息循环的常见 Win32 成语。
  • AppWindow 类与 DispatcherQueue 集成-当给定线程的 DispatcherQueue 正在关闭时,AppWindow 实例会自动销毁。
  • 它提供了一种注册在超时到期时调用的委托的方法。
  • 它提供事件,让组件知道消息循环何时退出,并选择性地延迟关闭,直到完成未完成的工作。 这可确保使用 DispatcherQueue 但并不拥有消息循环的组件能够在循环退出时在线程上执行清理。
  • DispatcherQueue 是一个线程单一实例(最多可以在任何给定线程上运行其中一个)。 默认情况下,线程没有 DispatcherQueue
  • 线程所有者可以创建 DispatcherQueueController 来初始化线程的 DispatcherQueue 。 此时,任何代码都可以访问线程的 DispatcherQueue;但只有 DispatcherQueueController 的所有者有权访问 DispatcherQueueController.ShutdownQueue 方法,该方法会清空 DispatcherQueue,并引发 ShutdownStartingShutdownCompleted 事件。
  • 最外部的消息循环所有者必须创建 DispatcherQueue 实例。 只有负责运行线程最外层消息循环的代码知道调度何时完成,这是关闭 DispatcherQueue 的适当时间。 这意味着依赖 DispatcherQueue 的组件不得创建 DispatcherQueue ,除非它们拥有线程的消息循环。

概述

线程退出其事件循环后,必须关闭其 DispatcherQueue。 这样做会引发 ShutdownStartingShutdownCompleted 事件,并在禁用进一步排队之前清空任何最终挂起的排队项。

调用 DispatcherQueueController.ShutdownQueueAsyncDispatcherQueueController.ShutdownQueue 时,引发的事件顺序如下:

  • 开始关机。 供应用程序处理。
  • FrameworkShutdownStarting。 供框架处理。
  • FrameworkShutdownCompleted。 供框架处理。
  • ShutdownCompleted。 供应用程序处理。

事件分为应用程序/框架类别,以便实现有序关闭。 也就是说,通过在框架关闭事件之前显式触发应用程序关闭,就不用担心在应用程序逐步关闭过程中框架组件会处于不可用状态。

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

最外层和递归消息循环

DispatcherQueue 支持自定义消息循环。 但是,对于不需要自定义的简单应用,我们提供了默认实现。 这消除了开发人员的负担,并帮助确保一致正确的行为。

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

系统调度器管理

某些 Windows 应用 SDK 组件(例如 MicaController)依赖于系统组件,而这些系统组件又要求在线程上运行系统 DispatcherQueueWindows.System.DispatcherQueue)。

在这些情况下,具有系统 DispatcherQueue 依赖项的组件调用 EnsureSystemDispatcherQueue 方法,从而释放应用管理系统 DispatcherQueue

调用此方法后,Windows 应用 SDK DispatcherQueue会自动管理系统DispatcherQueue的生存期, 关闭系统 DispatcherQueue 以及 Windows 应用 SDK DispatcherQueue。 组件可能同时依赖 Windows 应用 SDK 和系统 DispatcherQueue 关停事件,以确保它们在消息循环退出后进行适当的清理。

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

AppWindow 集成

AppWindow 类具有将其与 DispatcherQueue 集成的功能,以便在调用 DispatcherQueueController.ShutdownQueueAsyncDispatcherQueueController.ShutdownQueue 方法时自动销毁 AppWindow 对象。

AppWindow 还有一个属性,允许调用方检索与 AppWindow 关联的 DispatcherQueue;将其与 CompositionInput 命名空间中的其他对象对齐。

AppWindow 需要显式启用后才能感知到 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();
}