Skip to content

Commit fb00ace

Browse files
authored
New conceptual topic (#3744)
1 parent 55c45cd commit fb00ace

File tree

2 files changed

+187
-0
lines changed

2 files changed

+187
-0
lines changed

hub/apps/develop/dispatcherqueue.md

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
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+
```

hub/apps/toc.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -463,6 +463,8 @@ items:
463463
href: develop/security-and-identity.md
464464
- name: Speech
465465
href: develop/speech.md
466+
- name: DispatcherQueue
467+
href: develop/dispatcherqueue.md
466468
- name: Windows AI and machine learning
467469
href: /windows/ai/
468470
- name: Windows widget providers

0 commit comments

Comments
 (0)