|
| 1 | +--- |
| 2 | +title: DispatcherQueue |
| 3 | +description: Describes the purpose and function of the Windows App SDK DispatcherQueue class, and how to program with it. |
| 4 | +ms.date: 08/30/2023 |
| 5 | +ms.topic: article |
| 6 | +keywords: windows 11, windows 10, dispatcherqueue, dispatcherqueuecontroller |
| 7 | +ms.author: stwhi |
| 8 | +author: stevewhims |
| 9 | +ms.localizationpriority: high |
| 10 | +--- |
| 11 | + |
| 12 | +# DispatcherQueue |
| 13 | + |
| 14 | +## Highlights |
| 15 | + |
| 16 | +* The [DispatcherQueue](/windows/windows-app-sdk/api/winrt/microsoft.ui.dispatching.dispatcherqueue) class in the Windows App SDK manages a prioritized queue on which the tasks for a thread execute in a serial fashion. |
| 17 | +* It provides a means for background threads to run code on a **DispatcherQueue**'s thread (for example, the UI thread where objects with thread-affinity live). |
| 18 | +* The class precisely integrates with arbitrary message loops. For example, it supports the common Win32 idiom of nested message loops. |
| 19 | +* The [AppWindow](/windows/windows-app-sdk/api/winrt/microsoft.ui.windowing.appwindow) class integrates with **DispatcherQueue**—when a **DispatcherQueue** for a given thread is being shut down, the **AppWindow** instances are automatically destroyed. |
| 20 | +* It provides a means to register a delegate that's called when a timeout expires. |
| 21 | +* It provides events that let components know when a message loop is exiting, and optionally defer that shutdown until outstanding work completes. That ensures components that use the **DispatcherQueue**, but don't own the message loop, may do cleanup on-thread as the loop exits. |
| 22 | +* The **DispatcherQueue** is a thread singleton (there can be at most one of them running on any given thread). By default, a thread has no **DispatcherQueue**. |
| 23 | +* A thread owner may create a [DispatcherQueueController](/windows/windows-app-sdk/api/winrt/microsoft.ui.dispatching.dispatcherqueuecontroller) to initialize the **DispatcherQueue** for the thread. At that point, any code can access the thread's **DispatcherQueue**; but only the **DispatcherQueueController**'s owner has access to the [DispatcherQueueController.ShutdownQueueAsync](/windows/windows-app-sdk/api/winrt/microsoft.ui.dispatching.dispatcherqueuecontroller.shutdownqueueasync) method, which drains the |
| 24 | +**DispatcherQueue**, and raises **ShutdownStarted** and **ShutdownCompleted** events. |
| 25 | +* An outermost message loop owner must create a **DispatcherQueue** instance. Only the code in charge of running a thread's outermost message loop knows when dispatch is complete, which is the appropriate time to shut down the **DispatcherQueue**. That means that components that rely on **DispatcherQueue** mustn't create the **DispatcherQueue** unless they own the thread's message loop. |
| 26 | + |
| 27 | +## Run-down |
| 28 | + |
| 29 | +After a thread exits its event loop, it must shut down its **DispatcherQueue**. Doing so raises the **ShutdownStarting** and **ShutdownCompleted** events, and drains any final pending enqueued items before disabling further enqueuing. |
| 30 | + |
| 31 | +* To shut down a **DispatcherQueue** that's running on a dedicated thread with a **DispatcherQueue**-owned message loop, call the [DispatcherQueueController.ShutdownQueueAsync](/windows/windows-app-sdk/api/winrt/microsoft.ui.dispatching.dispatcherqueuecontroller.shutdownqueueasync) method. |
| 32 | +* For scenarios where the app owns an arbitrary message loop (for example, XAML Islands), call the synchronous [DispatcherQueueController.ShutdownQueue](/windows/windows-app-sdk/api/winrt/microsoft.ui.dispatching.dispatcherqueuecontroller.shutdownqueue) method. That method raises shutdown events, and drains the **DispatcherQueue** synchronously on the calling thread. |
| 33 | + |
| 34 | +When you call either **DispatcherQueueController.ShutdownQueueAsync** or **DispatcherQueueController.ShutdownQueue**, the order of events raised is the following: |
| 35 | + |
| 36 | +* **ShutdownStarting**. Intended for apps to handle. |
| 37 | +* **FrameworkShutdownStarting**. Intended for frameworks to handle. |
| 38 | +* **FrameworkShutdownCompleted**. Intended for frameworks to handle. |
| 39 | +* **ShutdownCompleted**. Intended for apps to handle. |
| 40 | + |
| 41 | +The events are separated into application/framework categories so that orderly shutdown can be achieved. That is, by explicitly raising application shutdown ahead of framework shutdown events, there's no danger that a framework component will be in an unusable state as the application winds down. |
| 42 | + |
| 43 | +```cppwinrt |
| 44 | +namespace winrt |
| 45 | +{ |
| 46 | + using namespace Microsoft::UI::Dispatching; |
| 47 | +} |
| 48 | +
|
| 49 | +// App runs its own custom message loop. |
| 50 | +void RunCustomMessageLoop() |
| 51 | +{ |
| 52 | + // Create a DispatcherQueue. |
| 53 | + auto dispatcherQueueController{winrt::DispatcherQueueController::CreateOnCurrentThread()}; |
| 54 | +
|
| 55 | + // Run a custom message loop. Runs until the message loop owner decides to stop. |
| 56 | + MSG msg; |
| 57 | + while (GetMessage(&msg, nullptr, 0, 0)) |
| 58 | + { |
| 59 | + if (!ContentPreTranslateMessage(&msg)) |
| 60 | + { |
| 61 | + TranslateMesasge(&msg); |
| 62 | + DispatchMessage(&msg); |
| 63 | + } |
| 64 | + } |
| 65 | +
|
| 66 | + // Run down the DispatcherQueue. This single call also runs down the system DispatcherQueue |
| 67 | + // if one was created via EnsureSystemDispatcherQueue: |
| 68 | + // 1. Raises DispatcherQueue.ShutdownStarting event. |
| 69 | + // 2. Drains remaining items in the DispatcherQueue, waits for deferrals. |
| 70 | + // 3. Raises DispatcherQueue.FrameworkShutdownStarting event. |
| 71 | + // 4. Drains remaining items in the DispatcherQueue, waits for deferrals. |
| 72 | + // 5. Disables further enqueuing. |
| 73 | + // 6. Raises the DispatcherQueue.FrameworkShutdownCompleted event. |
| 74 | + // 7. Raises the DispatcherQueue.ShutdownCompleted event. |
| 75 | +
|
| 76 | + dispatcherQueueController.ShutdownQueue(); |
| 77 | +} |
| 78 | +``` |
| 79 | + |
| 80 | +## Outermost and recursive message loops |
| 81 | + |
| 82 | +**DispatcherQueue** supports custom message loops. However, for simple apps that don't need customization, we provide a default implementations. That removes a burden from developers, and helps ensure consistently correct behavior. |
| 83 | + |
| 84 | +```cppwinrt |
| 85 | +namespace winrt |
| 86 | +{ |
| 87 | + using namespace Microsoft::UI::Dispatching; |
| 88 | +} |
| 89 | +
|
| 90 | +// Simple app; doesn't need a custom message loop. |
| 91 | +void RunMessageLoop() |
| 92 | +{ |
| 93 | + // Create a DispatcherQueue. |
| 94 | + auto dispatcherQueueController{winrt::DispatcherQueueController::CreateOnCurrentThread()}; |
| 95 | +
|
| 96 | + // Runs a message loop until a call to DispatcherQueue.EnqueueEventLoopExit or PostQuitMessage. |
| 97 | + dispatcherQueueController.DispatcherQueue().RunEventLoop(); |
| 98 | +
|
| 99 | + // Run down the DispatcherQueue. |
| 100 | + dispatcherQueueController.ShutdownQueue(); |
| 101 | +} |
| 102 | +
|
| 103 | +// May be called while receiving a message. |
| 104 | +void RunNestedLoop(winrt::DispatcherQueue dispatcherQueue) |
| 105 | +{ |
| 106 | + // Runs a message loop until a call to DispatcherQueue.EnqueueEventLoopExit or PostQuitMessage. |
| 107 | + dispatcherQueue.RunEventLoop(); |
| 108 | +} |
| 109 | +
|
| 110 | +// Called to break out of the message loop, returning from the RunEventLoop call lower down the |
| 111 | +// stack. |
| 112 | +void EndMessageLoop(winrt::DispatcherQueue dispatcherQueue) |
| 113 | +{ |
| 114 | + // Alternatively, calling Win32's PostQuitMessage has the same effect. |
| 115 | + dispatcherQueue.EnqueueEventLoopExit(); |
| 116 | +} |
| 117 | +``` |
| 118 | + |
| 119 | +## System dispatcher management |
| 120 | + |
| 121 | +Some Windows App SDK components (for example, [MicaController](/windows/windows-app-sdk/api/winrt/microsoft.ui.composition.systembackdrops.micacontroller)) depend on system components that in turn require a system **DispatcherQueue** (**Windows.System.DispatcherQueue**) running on the thread. |
| 122 | + |
| 123 | +In those cases, the component that has a system **DispatcherQueue** dependency calls the [EnsureSystemDispatcherQueue](/windows/windows-app-sdk/api/winrt/microsoft.ui.dispatching.dispatcherqueue.ensuresystemdispatcherqueue) method, freeing your app from managing a system **DispatcherQueue**. |
| 124 | + |
| 125 | +With that method called, the Windows App SDK **DispatcherQueue** manages the lifetime of the system **DispatcherQueue** automatically, shutting down the system **DispatcherQueue** |
| 126 | +alongside the Windows App SDK **DispatcherQueue**. Components might rely on both Windows App SDK and system **DispatcherQueue** shutdown events in order to ensure that they do proper cleanup after the message loop exits. |
| 127 | + |
| 128 | +```cppwinrt |
| 129 | +namespace winrt |
| 130 | +{ |
| 131 | + using namespace Microsoft::UI::Composition::SystemBackdrops; |
| 132 | + using namespace Microsoft::UI::Dispatching; |
| 133 | +} |
| 134 | +
|
| 135 | +// The Windows App SDK component calls this during its startup. |
| 136 | +void MicaControllerInitialize(winrt::DispatcherQueue dispatcherQueue) |
| 137 | +{ |
| 138 | + dispatcherQueue.EnsureSystemDispatcherQueue(); |
| 139 | +
|
| 140 | + // If the component needs the system DispatcherQueue explicitly, it can now grab it off the thread. |
| 141 | + winrt::Windows::System::DispatcherQueue systemDispatcherQueue = |
| 142 | + winrt::Windows::System::DispatcherQueue::GetForCurrentThread(); |
| 143 | +} |
| 144 | +
|
| 145 | +void AppInitialize() |
| 146 | +{ |
| 147 | + // App doesn't need to concern itself with the system DispatcherQueue dependency. |
| 148 | + auto micaController = winrt::MicaController(); |
| 149 | +} |
| 150 | +``` |
| 151 | + |
| 152 | +## AppWindow integration |
| 153 | + |
| 154 | +The [AppWindow](/windows/windows-app-sdk/api/winrt/microsoft.ui.windowing.appwindow) class has functionality that integrates it with the **DispatcherQueue**, so that **AppWindow** objects can automatically be destroyed when the [DispatcherQueueController.ShutdownQueueAsync](/windows/windows-app-sdk/api/winrt/microsoft.ui.dispatching.dispatcherqueuecontroller.shutdownqueueasync) or [DispatcherQueueController.ShutdownQueue](/windows/windows-app-sdk/api/winrt/microsoft.ui.dispatching.dispatcherqueuecontroller.shutdownqueue) method is called. |
| 155 | + |
| 156 | +There's also a property of **AppWindow** that allows callers to retrieve the **DispatcherQueue** associated with the **AppWindow**; aligning it with other objects in the **Composition** and **Input** namespaces. |
| 157 | + |
| 158 | +**AppWindow** needs your explicit opt-in in order to be aware of the **DispatcherQueue**. |
| 159 | + |
| 160 | +```cppwinrt |
| 161 | +namespace winrt |
| 162 | +{ |
| 163 | + using namespace Microsoft::UI::Dispatching; |
| 164 | + using namespace Microsoft::UI::Windowing; |
| 165 | +} |
| 166 | +
|
| 167 | +void Main() |
| 168 | +{ |
| 169 | + // Create a Windows App SDK DispatcherQueue. |
| 170 | + auto dispatcherQueueController{winrt::DispatcherQueueController::CreateOnCurrentThread()}; |
| 171 | +
|
| 172 | + var appWindow = AppWindow.Create(nullptr, 0, dispatcherQueueController.DispatcherQueue()); |
| 173 | +
|
| 174 | + // Since we associated the DispatcherQueue above with the AppWindow, we're able to retreive it |
| 175 | + // as a property. If we were to not associate a dispatcher, this property would be null. |
| 176 | + ASSERT(appWindow.DispatcherQueue() == dispatcherQueueController.DispatcherQueue()); |
| 177 | +
|
| 178 | + // Runs a message loop until a call to DispatcherQueue.EnqueueEventLoopExit or PostQuitMessage. |
| 179 | + dispatcherQueueController.DispatcherQueue().RunEventLoop(); |
| 180 | +
|
| 181 | + // Rundown the Windows App SDK DispatcherQueue. While this call is in progress, the AppWindow.Destoyed |
| 182 | + // event will be raised since the AppWindow instance is associated with the DispatcherQueue. |
| 183 | + dispatcherQueueController.ShutdownQueue(); |
| 184 | +} |
| 185 | +``` |
0 commit comments