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 時,所產生的事件順序如下:

  • 關機開始。 設計供應用程式處理。
  • 框架關閉開始。 設計給框架處理。
  • 框架關閉完成。 設計給框架處理。
  • 關機完成。 設計供應用程式處理。

事件會被細分為應用/框架類別,以便有序地關閉。 也就是說,藉由明確地在框架關閉事件發生之前觸發應用程式關閉,就不必擔心框架元件會在應用程式逐步關閉的過程中處於無法使用的狀態。

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 的生命週期,並在 Windows 應用程式 SDK DispatcherQueue 關閉時一併關閉系統 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;並將其與組合命名空間與輸入命名空間中的其他物件對齊。

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.Destroyed
    // event will be raised since the AppWindow instance is associated with the DispatcherQueue.
    dispatcherQueueController.ShutdownQueue();
}

參見