次の方法で共有


DispatcherQueue

概要

  • Windows アプリ SDKの DispatcherQueue クラスは、スレッドのタスクがシリアル形式で実行される優先順位付けされたキューを管理します。
  • これは、バックグラウンド スレッドが DispatcherQueue のスレッド (たとえば、スレッド アフィニティを持つオブジェクトがライブである UI スレッド) でコードを実行するための手段を提供します。
  • このクラスは、任意のメッセージ ループと正確に統合されます。 たとえば、入れ子になったメッセージ ループの一般的な Win32 イディオムがサポートされています。
  • AppWindow クラスは DispatcherQueue と統合されます。特定のスレッドの DispatcherQueue がシャットダウンされると、AppWindow インスタンスは自動的に破棄されます。
  • タイムアウトの有効期限が切れたときに呼び出されるデリゲートを登録する手段を提供します。
  • メッセージ ループがいつ終了するかをコンポーネントに通知し、必要に応じて未処理の作業が完了するまでシャットダウンを延期するイベントを提供します。 これにより、 DispatcherQueueを使用するが、メッセージ ループを所有していないコンポーネントは、ループの終了時にスレッド上でクリーンアップを実行できます。
  • DispatcherQueue はスレッド シングルトンです (指定されたスレッドで実行できるスレッドは最大 1 つです)。 既定では、スレッドには DispatcherQueue はありません。
  • スレッド所有者は、 DispatcherQueueController を作成して、スレッドの DispatcherQueue を初期化できます。 その時点で、任意のコードがスレッドの DispatcherQueue; にアクセスできます。 ただし、 DispatcherQueueController の所有者のみが DispatcherQueueController.ShutdownQueue メソッドにアクセスできます。このメソッドは、 DispatcherQueue をドレインし、 ShutdownStarted および shutdownCompleted イベントを発生させます。
  • 最も外側のメッセージ ループ所有者は、 DispatcherQueue インスタンスを作成する必要があります。 ディスパッチが完了したタイミングを認識するのは、スレッドの最も外側のメッセージ ループの実行を担当するコードだけです。これは、 DispatcherQueueをシャットダウンするのに適したタイミングです。 つまり、DispatcherQueue に依存するコンポーネントは、スレッドのメッセージ ループを所有していない限りDispatcherQueue を作成することはできません。

くたびれた

スレッドがイベント ループを終了した後、その DispatcherQueueをシャットダウンする必要があります。 これにより、 ShutdownStarting および ShutdownCompleted イベントが発生し、保留中のエンキューされた最終的な項目がドレインされてから、さらにエンキューが無効になります。

  • DispatcherQueue 所有メッセージ ループを使用して専用スレッドで実行されているDispatcherQueueをシャットダウンするには、DispatcherQueueController.ShutdownQueueAsync メソッドを呼び出します。
  • アプリが任意のメッセージ ループ (XAML Islands など) を所有しているシナリオでは、同期 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 の有効期間を自動的に管理し、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 と統合する機能があります。そのため、AppWindow オブジェクトは、DispatcherQueueController.ShutdownQueueAsyncまたはDispatcherQueueController.ShutdownQueueメソッドが呼び出されたときに自動的に破棄されます。

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();
}