DispatcherQueue
亮点
- Windows 应用 SDK中的 DispatcherQueue 类管理线程的任务以串行方式执行的优先级队列。
- 它为后台线程提供了一 种在 DispatcherQueue 线程上运行代码的方法(例如,具有线程相关性的对象所在的 UI 线程)。
- 该类与任意消息循环精确集成。 例如,它支持嵌套消息循环的常见 Win32 成语。
- AppWindow 类与 DispatcherQueue 集成-当给定线程的 DispatcherQueue 正在关闭时,AppWindow 实例会自动销毁。
- 它提供了一种注册在超时到期时调用的委托的方法。
- 它提供事件,让组件知道消息循环何时退出,并选择性地延迟关闭,直到完成未完成的工作。 这可确保使用 DispatcherQueue 但不拥有消息循环的组件可能会在循环退出时在线程上进行清理。
- DispatcherQueue 是一个线程单一实例(最多可以在任何给定线程上运行其中一个)。 默认情况下,线程没有 DispatcherQueue。
- 线程所有者可以创建 DispatcherQueueController 来初始化线程的 DispatcherQueue 。 此时,任何代码都可以访问线程的 DispatcherQueue;但只有 DispatcherQueueController 的所有者有权访问 DispatcherQueueController.ShutdownQueue 方法,该方法会清空 DispatcherQueue,并引发 ShutdownStarted 和 ShutdownCompleted 事件。
- 最外部的消息循环所有者必须创建 DispatcherQueue 实例。 只有负责运行线程最外层消息循环的代码知道调度何时完成,这是关闭 DispatcherQueue 的适当时间。 这意味着依赖 DispatcherQueue 的组件不得创建 DispatcherQueue,除非它们拥有线程的消息循环。
运行
线程退出其事件循环后,必须关闭其 DispatcherQueue。 这样做会 引发 ShutdownStarting 和 ShutdownCompleted 事件,并在禁用进一步排队之前清空任何最终挂起的排队项。
- 若要关闭在具有 DispatcherQueue 拥有的消息循环的专用线程上运行的 DispatcherQueue,请调用 DispatcherQueueController.ShutdownQueueAsync 方法。
- 对于应用拥有任意消息循环(例如 XAML 岛)的方案,请调用同步 DispatcherQueueController.ShutdownQueue 方法。 该方法引发关闭事件,并在调用线程上同步清空 DispatcherQueue 。
调用 DispatcherQueueController.ShutdownQueueAsync 或 DispatcherQueueController.ShutdownQueue 时,引发的事件顺序如下:
- ShutdownStarting。 适用于要处理的应用。
- 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))
{
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();
}
最外层和递归消息循环
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)依赖于系统组件,而系统组件又需要线程上运行的系统 DispatcherQueue(Windows.System.DispatcherQueue)。
在这些情况下,具有系统 DispatcherQueue 依赖项的组件调用 EnsureSystemDispatcherQueue 方法,从而释放应用管理系统 DispatcherQueue。
调用此方法后,Windows 应用 SDK DispatcherQueue 会自动管理系统 DispatcherQueue 的生存期,同时关闭 system 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.ShutdownQueueAsync 或 DispatcherQueueController.ShutdownQueue 方法时自动销毁 AppWindow 对象。
AppWindow 还有一个属性,它允许调用方检索与 AppWindow 关联的 DispatcherQueue;将其与 Composition 和 Input 命名空间中的其他对象对齐。
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()};
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();
}