DispatcherQueue
Советы
- Класс DispatcherQueue в пакете SDK для приложений Windows управляет приоритетной очередью, в которой задачи для потока выполняются последовательно.
- Он предоставляет средства для фонового потока для выполнения кода в потоке DispatcherQueue (например, поток пользовательского интерфейса, в котором объекты с динамическим сопоставлением потоков).
- Класс точно интегрируется с произвольными циклами сообщений. Например, он поддерживает распространенный идиом Win32 вложенных циклов сообщений.
- Класс AppWindow интегрируется с DispatcherQueue— при завершении работы диспетчера для заданного потока экземпляры AppWindow автоматически уничтожаются.
- Он предоставляет средства для регистрации делегата, вызываемого при истечении времени ожидания.
- Он предоставляет события, которые позволяют компонентам знать, когда цикл сообщений завершается, и при необходимости откладывает завершение работы до завершения невыполненных работ. Это гарантирует, что компоненты, использующие ДиспетчерQueue, но не принадлежащие циклу сообщений, могут выполнять очистку в потоке при выходе цикла.
- DispatcherQueue — это однопотоковый одноэлемент (может быть не более одного из них, выполняющегося в любом заданном потоке). По умолчанию поток не имеет dispatcherQueue.
- Владелец потока может создать диспетчерQueueController для инициализации DispatcherQueue для потока. На этом этапе любой код может получить доступ к методу DispatcherQueue потока, но только владелец DispatcherQueueController имеет доступ к методу DispatcherQueueController.ShutdownQueue, который очищает диспетчерqueue и вызывает события ShutdownStarted и ShutdownCompleted.
- Самый внешний владелец цикла сообщений должен создать экземпляр DispatcherQueue . Только код, отвечающий за выполнение самого внешнего цикла сообщений потока, знает, когда выполняется отправка, что является подходящим временем для завершения диспетчера. Это означает, что компоненты, которые полагаются на DispatcherQueue, не должны создавать ДиспетчерQueue, если они не принадлежат циклу сообщений потока.
Запуск
После завершения цикла событий поток должен завершить работу диспетчера. Это вызывает события ShutdownStarting и ShutdownCompleted и удаляет все конечные ожидающие элементы, прежде чем отключать дальнейшие запросы.
- Чтобы завершить работу диспетчера, работающего в выделенном потоке с циклом сообщений, принадлежащим DispatcherQueue, вызовите метод DispatcherQueueController.ShutdownQueueAsync.
- В сценариях, когда приложение владеет произвольным циклом сообщений (например, XAML Islands), вызовите синхронный метод DispatcherQueueController.ShutdownQueueQueue . Этот метод вызывает события завершения работы и выполняет синхронную очистку ДиспетчераQueue в вызывающем потоке.
При вызове dispatcherQueueController.ShutdownQueueAsync или DispatcherQueueController.ShutdownQueue порядок событий, вызванных следующим образом:
- Завершение работы. Предназначено для обработки приложений.
- FrameworkShutdownStarting. Предназначено для обработки платформ.
- FrameworkShutdownCompleted. Предназначено для обработки платформ.
- Завершение работы. Предназначено для обработки приложений.
События разделены на категории приложений и платформы, чтобы обеспечить упорядоченное завершение работы. То есть, явно вызывая завершение работы приложения перед событиями завершения работы платформы, нет никакой опасности, что компонент платформы будет находиться в неиспользуемом состоянии, так как приложение завершает работу.
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();
}
Управление системным диспетчером
Некоторые компоненты пакета SDK для приложений Windows (например, MicaController) зависят от системных компонентов, которые, в свою очередь, требуют системного диспетчера (Windows.System.DispatcherQueue), работающего в потоке.
В таких случаях компонент, имеющий системную зависимость DispatcherQueue , вызывает метод EnsureSystemDispatcherQueue , освобождая приложение от управления системным диспетчеромQueue.
При вызове этого метода диспетчер пакета SDK для приложений Windows ДиспетчерQueue управляет временем существования системы DispatcherQueue автоматически, завершив работу системы DispatcherQueue вместе с диспетчером пакета SDK для приложений Windows. Компоненты могут полагаться на события завершения работы пакета SDK для приложений Windows и system 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 имеет функциональные возможности, которые интегрируются с диспетчеромQueue, поэтому объекты AppWindow могут автоматически быть уничтожены при вызове метода DispatcherQueueController.ShutdownQueueAsync или DispatcherQueueController.ShutdownQueueQueue.
Существует также свойство AppWindow, позволяющее вызывающим пользователям получать ДиспетчерQueue, связанный с AppWindow; выравнивая его с другими объектами в пространствах имен композиции и ввода.
AppWindow требует явного согласия, чтобы знать о диспетчереQueue.
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();
}
Windows developer